diff --git a/src/Neo/Cryptography/BN254.cs b/src/Neo/Cryptography/BN254.cs new file mode 100644 index 0000000000..e4958b3fab --- /dev/null +++ b/src/Neo/Cryptography/BN254.cs @@ -0,0 +1,277 @@ +// Copyright (C) 2015-2025 The Neo Project. +// +// BN254.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Nethermind.MclBindings; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Neo.Cryptography +{ + public static class BN254 + { + public const int FieldElementLength = 32; + public const int G1EncodedLength = 64; + public const int PairInputLength = 192; + + private static readonly object s_sync = new(); + private static volatile bool s_initialized; + + public static byte[] Add(ReadOnlySpan input) + { + EnsureInitialized(); + + if (!TryDeserializeG1(input[..G1EncodedLength], out var first)) + return new byte[G1EncodedLength]; + + if (!TryDeserializeG1(input[G1EncodedLength..], out var second)) + return new byte[G1EncodedLength]; + + mclBnG1 result = default; + Mcl.mclBnG1_add(ref result, first, second); + Mcl.mclBnG1_normalize(ref result, result); + + return SerializeG1(result); + } + + public static byte[] Mul(ReadOnlySpan input) + { + EnsureInitialized(); + + if (!TryDeserializeG1(input[..G1EncodedLength], out var basePoint)) + return new byte[G1EncodedLength]; + + if (!TryDeserializeScalar(input[G1EncodedLength..], out var scalar)) + return new byte[G1EncodedLength]; + + mclBnG1 result = default; + Mcl.mclBnG1_mul(ref result, basePoint, scalar); + Mcl.mclBnG1_normalize(ref result, result); + + return SerializeG1(result); + } + + public static byte[] Pairing(ReadOnlySpan input) + { + EnsureInitialized(); + + if (input.Length == 0) + return SuccessWord(); + + int pairCount = input.Length / PairInputLength; + bool hasEffectivePair = false; + + mclBnGT accumulator = default; + Mcl.mclBnGT_setInt32(ref accumulator, 1); + + for (int pairIndex = 0; pairIndex < pairCount; pairIndex++) + { + int offset = pairIndex * PairInputLength; + var g1Slice = input.Slice(offset, G1EncodedLength); + var g2Slice = input.Slice(offset + G1EncodedLength, 2 * G1EncodedLength); + + if (!TryDeserializeG1(g1Slice, out var g1)) + return new byte[FieldElementLength]; + + if (!TryDeserializeG2(g2Slice, out var g2)) + return new byte[FieldElementLength]; + + if (Mcl.mclBnG1_isZero(g1) == 1 || Mcl.mclBnG2_isZero(g2) == 1) + continue; + + hasEffectivePair = true; + + mclBnGT current = default; + Mcl.mclBn_pairing(ref current, g1, g2); + + if (Mcl.mclBnGT_isValid(current) == 0) + return new byte[FieldElementLength]; + + mclBnGT temp = accumulator; + Mcl.mclBnGT_mul(ref accumulator, temp, current); + } + + if (!hasEffectivePair) + return SuccessWord(); + + return Mcl.mclBnGT_isOne(accumulator) == 1 ? SuccessWord() : new byte[FieldElementLength]; + } + + private static unsafe bool TryDeserializeG1(ReadOnlySpan encoded, out mclBnG1 point) + { + point = default; + + if (IsAllZero(encoded)) + return true; + + ReadOnlySpan xBytes = encoded[..FieldElementLength]; + fixed (byte* ptr = xBytes) + { + if (Mcl.mclBnFp_setBigEndianMod(ref point.x, (nint)ptr, (nuint)xBytes.Length) != 0) + return false; + } + + ReadOnlySpan yBytes = encoded[FieldElementLength..]; + fixed (byte* ptr = yBytes) + { + if (Mcl.mclBnFp_setBigEndianMod(ref point.y, (nint)ptr, (nuint)yBytes.Length) != 0) + return false; + } + + Mcl.mclBnFp_setInt32(ref point.z, 1); + + return Mcl.mclBnG1_isValid(point) == 1; + } + + private static unsafe bool TryDeserializeScalar(ReadOnlySpan encoded, out mclBnFr scalar) + { + scalar = default; + + if (IsAllZero(encoded)) + { + Mcl.mclBnFr_clear(ref scalar); + return true; + } + + fixed (byte* ptr = encoded) + { + if (Mcl.mclBnFr_setBigEndianMod(ref scalar, (nint)ptr, (nuint)encoded.Length) == -1) + return false; + } + + return Mcl.mclBnFr_isValid(scalar) == 1; + } + + private static unsafe bool TryDeserializeG2(ReadOnlySpan encoded, out mclBnG2 point) + { + point = default; + + if (IsAllZero(encoded)) + return true; + + Span scratch = stackalloc byte[FieldElementLength]; + + var realSegment = encoded.Slice(FieldElementLength, FieldElementLength); + CopyReversed(realSegment, scratch); + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_deserialize(ref point.x.d0, (nint)ptr, (nuint)scratch.Length) == UIntPtr.Zero) + return false; + } + + var imagSegment = encoded[..FieldElementLength]; + CopyReversed(imagSegment, scratch); + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_deserialize(ref point.x.d1, (nint)ptr, (nuint)scratch.Length) == UIntPtr.Zero) + return false; + } + + var yReal = encoded.Slice(3 * FieldElementLength, FieldElementLength); + CopyReversed(yReal, scratch); + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_deserialize(ref point.y.d0, (nint)ptr, (nuint)scratch.Length) == UIntPtr.Zero) + return false; + } + + var yImag = encoded.Slice(2 * FieldElementLength, FieldElementLength); + CopyReversed(yImag, scratch); + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_deserialize(ref point.y.d1, (nint)ptr, (nuint)scratch.Length) == UIntPtr.Zero) + return false; + } + + Mcl.mclBnFp_setInt32(ref point.z.d0, 1); + + return true; + } + + private static unsafe byte[] SerializeG1(in mclBnG1 point) + { + var output = new byte[G1EncodedLength]; + + if (Mcl.mclBnG1_isZero(point) == 1) + return output; + + Span scratch = stackalloc byte[FieldElementLength]; + + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_getLittleEndian((nint)ptr, (nuint)scratch.Length, point.x) == UIntPtr.Zero) + throw new ArgumentException("Failed to serialize BN254 point"); + } + + WriteBigEndian(scratch, output.AsSpan(0, FieldElementLength)); + + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_getLittleEndian((nint)ptr, (nuint)scratch.Length, point.y) == UIntPtr.Zero) + throw new ArgumentException("Failed to serialize BN254 point"); + } + + WriteBigEndian(scratch, output.AsSpan(FieldElementLength, FieldElementLength)); + + return output; + } + + private static byte[] SuccessWord() + { + var output = new byte[FieldElementLength]; + output[^1] = 1; + return output; + } + + private static bool IsAllZero(ReadOnlySpan data) + { + for (int i = 0; i < data.Length; ++i) + { + if (data[i] != 0) + return false; + } + + return true; + } + + private static void WriteBigEndian(ReadOnlySpan littleEndian, Span destination) + { + for (int i = 0; i < littleEndian.Length; ++i) + destination[i] = littleEndian[littleEndian.Length - 1 - i]; + } + + private static void CopyReversed(ReadOnlySpan source, Span destination) + { + for (int i = 0; i < source.Length; ++i) + destination[i] = source[source.Length - 1 - i]; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void EnsureInitialized() + { + if (s_initialized) + return; + + lock (s_sync) + { + if (s_initialized) + return; + + if (Mcl.mclBn_init(Mcl.MCL_BN_SNARK1, Mcl.MCLBN_COMPILED_TIME_VAR) != 0) + throw new InvalidOperationException("BN254 initialization failed"); + + Mcl.mclBn_setETHserialization(1); + + s_initialized = true; + } + } + } +} diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj index 837011162c..c90c82f610 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs new file mode 100644 index 0000000000..69695d40e3 --- /dev/null +++ b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs @@ -0,0 +1,49 @@ +// Copyright (C) 2015-2025 The Neo Project. +// +// CryptoLib.BN254.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Cryptography; +using System; + +namespace Neo.SmartContract.Native +{ + partial class CryptoLib + { + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + public static byte[] Bn254Add(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); + if (input.Length != BN254.G1EncodedLength * 2) + throw new ArgumentException("Invalid BN254 add input length", nameof(input)); + + return BN254.Add(input); + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + public static byte[] Bn254Mul(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); + if (input.Length != BN254.G1EncodedLength + BN254.FieldElementLength) + throw new ArgumentException("Invalid BN254 mul input length", nameof(input)); + + return BN254.Mul(input); + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] + public static byte[] Bn254Pairing(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); + if (input.Length % BN254.PairInputLength != 0) + throw new ArgumentException("Invalid BN254 pairing input length", nameof(input)); + + return BN254.Pairing(input); + } + } +} diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index e70b35083d..145b4097e5 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -56,6 +56,21 @@ public class UT_CryptoLib private readonly byte[] g2 = s_g2Hex.HexToBytes(); private readonly byte[] gt = s_gtHex.HexToBytes(); + private const string Bn254G1X = "1"; + private const string Bn254G1Y = "2"; + private const string Bn254G2XIm = "1800deef121f1e7641a819fe67140f7f8f87b140996fbbd1ba87fb145641f404"; + private const string Bn254G2XRe = "198e9393920d483a7260bfb731fb5db382322bc5b47fbf6c80f6321231df581"; + private const string Bn254G2YIm = "12c85ea5db8c6deb43baf7868f1c5341fd8ed84a82f89ed36e80b6a4a8dd22e1"; + private const string Bn254G2YRe = "090689d0585ff0756c27a122072274f89d4d1c6d2f9d3af03d86c6b29b53e2b"; + private const string Bn254DoubleX = "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3"; + private const string Bn254DoubleY = "15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"; + private const string Bn254PairingPositive = + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e104316c97997c17267a1bb67365523b4388e1306d66ea6e4d8f4a4a4b65f5c7d06e286b49c56f6293b2cea30764f0d5eabe5817905468a41f09b77588f692e8b081070efe3d4913dde35bba2513c426d065dee815c478700cef07180fb6146182432428b1490a4f25053d4c20c8723a73de6f0681bd3a8fca41008a6c3c288252d50f18403272e96c10135f96db0f8d0aec25033ebdffb88d2e7956c9bb198ec072462211ebc0a2f042f993d5bd76caf4adb5e99610dcf7c1d992595e6976aa3"; + private const string Bn254PairingNegative = + "0x142c9123c08a0d7f66d95f3ad637a06b95700bc525073b75610884ef45416e1610104c796f40bfeef3588e996c040d2a88c0b4b85afd2578327b99413c6fe820198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d"; + private const string Bn254PairingInvalidG1 = + "0x00000000000000000000000000000000000000000000000000000000000000000000000000be00be00bebebebebebe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + private readonly byte[] notG1 = "8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" @@ -1152,6 +1167,103 @@ public void TestVerifyWithEd25519() Assert.IsFalse(CallVerifyWithEd25519(message, invalidPublicKey, signature)); } + [TestMethod] + public void TestBn254Add() + { + byte[] input = new byte[128]; + WriteBn254Field(Bn254G1X, input, 0); + WriteBn254Field(Bn254G1Y, input, 32); + WriteBn254Field(Bn254G1X, input, 64); + WriteBn254Field(Bn254G1Y, input, 96); + + byte[] result = CryptoLib.Bn254Add(input); + + byte[] expected = new byte[64]; + WriteBn254Field(Bn254DoubleX, expected, 0); + WriteBn254Field(Bn254DoubleY, expected, 32); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void TestBn254Mul() + { + byte[] input = new byte[96]; + WriteBn254Field(Bn254G1X, input, 0); + WriteBn254Field(Bn254G1Y, input, 32); + WriteBn254Field("2", input, 64); + + byte[] result = CryptoLib.Bn254Mul(input); + + byte[] expected = new byte[64]; + WriteBn254Field(Bn254DoubleX, expected, 0); + WriteBn254Field(Bn254DoubleY, expected, 32); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void TestBn254PairingGenerator() + { + byte[] input = new byte[192]; + WriteBn254Field(Bn254G1X, input, 0); + WriteBn254Field(Bn254G1Y, input, 32); + WriteBn254Field(Bn254G2XIm, input, 64); + WriteBn254Field(Bn254G2XRe, input, 96); + WriteBn254Field(Bn254G2YIm, input, 128); + WriteBn254Field(Bn254G2YRe, input, 160); + + byte[] result = CryptoLib.Bn254Pairing(input); + + Assert.IsTrue(result.All(b => b == 0)); + } + + [TestMethod] + public void TestBn254PairingEmpty() + { + byte[] result = CryptoLib.Bn254Pairing(Array.Empty()); + Assert.IsTrue(result.Take(result.Length - 1).All(b => b == 0)); + Assert.AreEqual(1, result[^1]); + } + + [TestMethod] + public void TestBn254PairingVectors() + { + // Vectors sourced from ethereum/tests (MIT) GeneralStateTests/stZeroKnowledge/ecpairing_inputs.json + // commit c67e485ff8b5be9abc8ad15345ec21aa22e290d9 labels 5 (positive), 0 (negative), 38 (invalid_g1_point) + var cases = new (string Hex, bool ExpectedSuccess, string Label)[] + { + (Bn254PairingPositive, true, "positive"), + (Bn254PairingNegative, false, "negative"), + (Bn254PairingInvalidG1, false, "invalid_g1_point"), + }; + + foreach (var (hex, expectedSuccess, label) in cases) + { + byte[] input = HexToBytes(hex); + byte[] result = CryptoLib.Bn254Pairing(input); + + Assert.AreEqual(32, result.Length, label); + if (expectedSuccess) + { + Assert.AreEqual(1, result[^1], label); + Assert.IsTrue(result.Take(result.Length - 1).All(b => b == 0), label); + } + else + { + Assert.IsTrue(result.All(b => b == 0), label); + } + } + } + + [TestMethod] + public void TestBn254InvalidInputs() + { + Assert.ThrowsExactly(() => CryptoLib.Bn254Add(Array.Empty())); + Assert.ThrowsExactly(() => CryptoLib.Bn254Mul(Array.Empty())); + Assert.ThrowsExactly(() => CryptoLib.Bn254Pairing(new byte[1])); + } + private bool CallVerifyWithEd25519(byte[] message, byte[] publicKey, byte[] signature) { var snapshot = TestBlockchain.GetTestSnapshotCache(); @@ -1174,5 +1286,27 @@ private bool CallVerifyWithEd25519(byte[] message, byte[] publicKey, byte[] sign return engine.ResultStack.Pop().GetBoolean(); } } + + private static void WriteBn254Field(string hex, byte[] buffer, int offset) + { + var field = Bn254Field(hex); + Buffer.BlockCopy(field, 0, buffer, offset, field.Length); + } + + private static byte[] Bn254Field(string hex) + { + if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + hex = hex[2..]; + if (hex.Length > 64) + throw new ArgumentOutOfRangeException(nameof(hex)); + return hex.PadLeft(64, '0').HexToBytes(); + } + + private static byte[] HexToBytes(string hex) + { + if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + hex = hex[2..]; + return Convert.FromHexString(hex); + } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 40026d9783..be899373b1 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -42,7 +42,7 @@ public void TestSetup() { {"ContractManagement", """{"id":-1,"updatecounter":0,"hash":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":3581846399},"manifest":{"name":"ContractManagement","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Array","offset":0,"safe":false},{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Array","offset":7,"safe":false},{"name":"destroy","parameters":[],"returntype":"Void","offset":14,"safe":false},{"name":"getContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Array","offset":21,"safe":true},{"name":"getContractById","parameters":[{"name":"id","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getContractHashes","parameters":[],"returntype":"InteropInterface","offset":35,"safe":true},{"name":"getMinimumDeploymentFee","parameters":[],"returntype":"Integer","offset":42,"safe":true},{"name":"hasMethod","parameters":[{"name":"hash","type":"Hash160"},{"name":"method","type":"String"},{"name":"pcount","type":"Integer"}],"returntype":"Boolean","offset":49,"safe":true},{"name":"isContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Boolean","offset":56,"safe":true},{"name":"setMinimumDeploymentFee","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":63,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","offset":70,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false}],"events":[{"name":"Deploy","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Update","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Destroy","parameters":[{"name":"Hash","type":"Hash160"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}""" }, {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":2426471238},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"base64UrlDecode","parameters":[{"name":"s","type":"String"}],"returntype":"String","offset":56,"safe":true},{"name":"base64UrlEncode","parameters":[{"name":"data","type":"String"}],"returntype":"String","offset":63,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":70,"safe":true},{"name":"hexDecode","parameters":[{"name":"str","type":"String"}],"returntype":"ByteArray","offset":77,"safe":true},{"name":"hexEncode","parameters":[{"name":"bytes","type":"ByteArray"}],"returntype":"String","offset":84,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":91,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":98,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":105,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":112,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":119,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":126,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":133,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":140,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":147,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":154,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":161,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":168,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, - {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":174904780},"manifest":{"name":"CryptoLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"bls12381Add","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":0,"safe":true},{"name":"bls12381Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":7,"safe":true},{"name":"bls12381Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":14,"safe":true},{"name":"bls12381Mul","parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"bls12381Pairing","parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","offset":28,"safe":true},{"name":"bls12381Serialize","parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","offset":35,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":49,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":56,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":77,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":84,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":1841570703},"manifest":{"name":"CryptoLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"bls12381Add","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":0,"safe":true},{"name":"bls12381Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":7,"safe":true},{"name":"bls12381Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":14,"safe":true},{"name":"bls12381Mul","parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"bls12381Pairing","parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","offset":28,"safe":true},{"name":"bls12381Serialize","parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","offset":35,"safe":true},{"name":"bn254Add","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"bn254Mul","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":49,"safe":true},{"name":"bn254Pairing","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":56,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":77,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":84,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":91,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":98,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":105,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"LedgerContract", """{"id":-4,"updatecounter":0,"hash":"0xda65b600f7124ce6c79950c1772a36403104f2be","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"LedgerContract","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"currentHash","parameters":[],"returntype":"Hash256","offset":0,"safe":true},{"name":"currentIndex","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getBlock","parameters":[{"name":"indexOrHash","type":"ByteArray"}],"returntype":"Array","offset":14,"safe":true},{"name":"getTransaction","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":21,"safe":true},{"name":"getTransactionFromBlock","parameters":[{"name":"blockIndexOrHash","type":"ByteArray"},{"name":"txIndex","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getTransactionHeight","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":35,"safe":true},{"name":"getTransactionSigners","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":42,"safe":true},{"name":"getTransactionVMState","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":49,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"NeoToken", """{"id":-5,"updatecounter":0,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"NeoToken","groups":[],"features":{},"supportedstandards":["NEP-17","NEP-27"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getAccountState","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","offset":14,"safe":true},{"name":"getAllCandidates","parameters":[],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"getCandidateVote","parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","offset":28,"safe":true},{"name":"getCandidates","parameters":[],"returntype":"Array","offset":35,"safe":true},{"name":"getCommittee","parameters":[],"returntype":"Array","offset":42,"safe":true},{"name":"getCommitteeAddress","parameters":[],"returntype":"Hash160","offset":49,"safe":true},{"name":"getGasPerBlock","parameters":[],"returntype":"Integer","offset":56,"safe":true},{"name":"getNextBlockValidators","parameters":[],"returntype":"Array","offset":63,"safe":true},{"name":"getRegisterPrice","parameters":[],"returntype":"Integer","offset":70,"safe":true},{"name":"onNEP17Payment","parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false},{"name":"registerCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":84,"safe":false},{"name":"setGasPerBlock","parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","offset":91,"safe":false},{"name":"setRegisterPrice","parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","offset":98,"safe":false},{"name":"symbol","parameters":[],"returntype":"String","offset":105,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":112,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":119,"safe":false},{"name":"unclaimedGas","parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","offset":126,"safe":true},{"name":"unregisterCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":133,"safe":false},{"name":"vote","parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","offset":140,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"GasToken", """{"id":-6,"updatecounter":0,"hash":"0xd2a4cff31913016155e38e474a2c06d08be276cf","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2663858513},"manifest":{"name":"GasToken","groups":[],"features":{},"supportedstandards":["NEP-17"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"symbol","parameters":[],"returntype":"String","offset":14,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":21,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":28,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""},