From 9973b987b5ae7cb73563a2588f048e1693da70c9 Mon Sep 17 00:00:00 2001 From: jimmy Date: Sat, 8 Nov 2025 00:48:35 +0800 Subject: [PATCH 1/9] Add BN254 native contract --- src/Neo/Cryptography/BN254.cs | 276 ++++++++++++++++++ src/Neo/Neo.csproj | 1 + .../SmartContract/Native/CryptoLib.BN254.cs | 43 +++ tests/Neo.UnitTests/Neo.UnitTests.csproj | 3 + .../Native/BN254TestVectors/bn256Add.json | 114 ++++++++ .../Native/BN254TestVectors/bn256Pairing.json | 100 +++++++ .../BN254TestVectors/bn256ScalarMul.json | 135 +++++++++ .../SmartContract/Native/UT_CryptoLib.cs | 216 ++++++++++++++ .../SmartContract/Native/UT_NativeContract.cs | 2 +- 9 files changed, 889 insertions(+), 1 deletion(-) create mode 100644 src/Neo/Cryptography/BN254.cs create mode 100644 src/Neo/SmartContract/Native/CryptoLib.BN254.cs create mode 100644 tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Add.json create mode 100644 tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Pairing.json create mode 100644 tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256ScalarMul.json diff --git a/src/Neo/Cryptography/BN254.cs b/src/Neo/Cryptography/BN254.cs new file mode 100644 index 0000000000..d80a521239 --- /dev/null +++ b/src/Neo/Cryptography/BN254.cs @@ -0,0 +1,276 @@ +// 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 Neo.Extensions; +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 bool s_initialized; + + public static byte[] Add(ReadOnlySpan input) + { + if (input.Length != G1EncodedLength * 2) + throw new ArgumentException("Invalid BN254 add input length", nameof(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) + { + if (input.Length != G1EncodedLength + FieldElementLength) + throw new ArgumentException("Invalid BN254 mul input length", nameof(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) + { + if (input.Length % PairInputLength != 0) + throw new ArgumentException("Invalid BN254 pairing input length", nameof(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 (!encoded.NotZero()) + 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 (!encoded.NotZero()) + { + 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 (!encoded.NotZero()) + 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 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 89da35b62d..5310c78d0e 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -9,6 +9,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..e54959a4c4 --- /dev/null +++ b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs @@ -0,0 +1,43 @@ +// 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); + + return BN254.Add(input); + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + public static byte[] Bn254Mul(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); + + return BN254.Mul(input); + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] + public static byte[] Bn254Pairing(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); + + return BN254.Pairing(input); + } + } +} diff --git a/tests/Neo.UnitTests/Neo.UnitTests.csproj b/tests/Neo.UnitTests/Neo.UnitTests.csproj index 61d935b787..2bc024efd8 100644 --- a/tests/Neo.UnitTests/Neo.UnitTests.csproj +++ b/tests/Neo.UnitTests/Neo.UnitTests.csproj @@ -17,6 +17,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Add.json b/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Add.json new file mode 100644 index 0000000000..b6fcd550e3 --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Add.json @@ -0,0 +1,114 @@ +[ + { + "Input": "18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f3726607c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + "Expected": "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + "Name": "chfast1", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c91518b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", + "Expected": "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204", + "Name": "chfast2", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio1", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio2", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio3", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio4", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio5", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Name": "cdetrio6", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Name": "cdetrio7", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Name": "cdetrio8", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Gas": 150, + "Name": "cdetrio9", + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Gas": 150, + "Name": "cdetrio10", + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "Expected": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + "Name": "cdetrio11", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + "Name": "cdetrio12", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + "Expected": "15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66049c797f9ce0d17083deb32b5e36f2ea2a212ee036598dd7624c168993d1355f", + "Name": "cdetrio13", + "Gas": 150, + "NoBenchmark": false + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "cdetrio14", + "Gas": 150, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Pairing.json b/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Pairing.json new file mode 100644 index 0000000000..3fbed6b87c --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256Pairing.json @@ -0,0 +1,100 @@ +[ + { + "Input": "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff1", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff2", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee245901b9e027bd5cfc2cb5db82d4dc9677ac795ec500ecd47deee3b5da006d6d049b811d7511c78158de484232fc68daf8a45cf217d1c2fae693ff5871e8752d73b21198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff3", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "2f2ea0b3da1e8ef11914acf8b2e1b32d99df51f5f4f206fc6b947eae860eddb6068134ddb33dc888ef446b648d72338684d678d2eb2371c61a50734d78da4b7225f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb122acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf6806d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb186bac5188a98c45e6016873d107f5cd131f3a3e339d0375e58bd6219347b008122ae2b09e539e152ec5364e7e2204b03d11d3caa038bfc7cd499f8176aacbee1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd415794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f211b88da1679b0b64a63b7e0e7bfe52aae524f73a55be7fe70c7e9bfc94b4cf0da1213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff4", + "Gas": 147000, + "NoBenchmark": false + }, + { + "Input": "20a754d2071d4d53903e3b31a7e98ad6882d58aec240ef981fdf0a9d22c5926a29c853fcea789887315916bbeb89ca37edb355b4f980c9a12a94f30deeed30211213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f1abb4a25eb9379ae96c84fff9f0540abcfc0a0d11aeda02d4f37e4baf74cb0c11073b3ff2cdbb38755f8691ea59e9606696b3ff278acfc098fa8226470d03869217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac290a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a98552fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d70f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2198a1f162a73261f112401aa2db79c7dab1533c9935c77290a6ce3b191f2318d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "jeff5", + "Gas": 147000, + "NoBenchmark": false + }, + { + "Input": "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c103188585e2364128fe25c70558f1560f4f9350baf3959e603cc91486e110936198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "jeff6", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "empty_data", + "Gas": 45000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000000", + "Name": "one_point", + "Gas": 79000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "two_point_match_2", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "two_point_match_3", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "two_point_match_4", + "Gas": 113000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_1", + "Gas": 385000, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_2", + "Gas": 385000, + "NoBenchmark": false + }, + { + "Input": "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", + "Expected": "0000000000000000000000000000000000000000000000000000000000000001", + "Name": "ten_point_match_3", + "Gas": 113000, + "NoBenchmark": false + } +] \ No newline at end of file diff --git a/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256ScalarMul.json b/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256ScalarMul.json new file mode 100644 index 0000000000..b0427fcc05 --- /dev/null +++ b/tests/Neo.UnitTests/SmartContract/Native/BN254TestVectors/bn256ScalarMul.json @@ -0,0 +1,135 @@ +[ + { + "Input": "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb20400000000000000000000000000000000000000000000000011138ce750fa15c2", + "Expected": "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + "Name": "chfast1", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46", + "Expected": "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e", + "Name": "chfast2", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3", + "Expected": "14789d0d4a730b354403b5fac948113739e276c23e0258d8596ee72f9cd9d3230af18a63153e0ec25ff9f2951dd3fa90ed0197bfef6e2a1a62b5095b9d2b4a27", + "Name": "chfast3", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11", + "Name": "cdetrio1", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + "Expected": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451", + "Name": "cdetrio2", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000100000000000000000000000000000000", + "Expected": "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", + "Name": "cdetrio3", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000009", + "Expected": "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575", + "Name": "cdetrio4", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000001", + "Expected": "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6", + "Name": "cdetrio5", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "29e587aadd7c06722aabba753017c093f70ba7eb1f1c0104ec0564e7e3e21f6022b1143f6a41008e7755c71c3d00b6b915d386de21783ef590486d8afa8453b1", + "Name": "cdetrio6", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + "Expected": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb", + "Name": "cdetrio7", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000100000000000000000000000000000000", + "Expected": "221a3577763877920d0d14a91cd59b9479f83b87a653bb41f82a3f6f120cea7c2752c7f64cdd7f0e494bff7b60419f242210f2026ed2ec70f89f78a4c56a1f15", + "Name": "cdetrio8", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000000000000009", + "Expected": "228e687a379ba154554040f8821f4e41ee2be287c201aa9c3bc02c9dd12f1e691e0fd6ee672d04cfd924ed8fdc7ba5f2d06c53c1edc30f65f2af5a5b97f0a76a", + "Name": "cdetrio9", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000000000000001", + "Expected": "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c", + "Name": "cdetrio10", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "Expected": "00a1a234d08efaa2616607e31eca1980128b00b415c845ff25bba3afcb81dc00242077290ed33906aeb8e42fd98c41bcb9057ba03421af3f2d08cfc441186024", + "Name": "cdetrio11", + "Gas": 6000, + "NoBenchmark": false + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d9830644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + "Expected": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b8692929ee761a352600f54921df9bf472e66217e7bb0cee9032e00acc86b3c8bfaf", + "Name": "cdetrio12", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000100000000000000000000000000000000", + "Expected": "1071b63011e8c222c5a771dfa03c2e11aac9666dd097f2c620852c3951a4376a2f46fe2f73e1cf310a168d56baa5575a8319389d7bfa6b29ee2d908305791434", + "Name": "cdetrio13", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000009", + "Expected": "19f75b9dd68c080a688774a6213f131e3052bd353a304a189d7a2ee367e3c2582612f545fb9fc89fde80fd81c68fc7dcb27fea5fc124eeda69433cf5c46d2d7f", + "Name": "cdetrio14", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000001", + "Expected": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + "Name": "cdetrio15", + "Gas": 6000, + "NoBenchmark": true + }, + { + "Input": "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000000", + "Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Name": "zeroScalar", + "Gas": 6000, + "NoBenchmark": true + } +] \ No newline at end of file diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index e70b35083d..167c55ebef 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -23,8 +23,11 @@ using Org.BouncyCastle.Utilities.Encoders; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; +using System.Text.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace Neo.UnitTests.SmartContract.Native { @@ -56,6 +59,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 +1170,157 @@ 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])); + } + + [TestMethod] + public void TestBn254AddGethVectors() + { + foreach (var vector in LoadGethPrecompileVectors("bn256Add.json")) + { + byte[] input = HexToBytes(vector.Input); + if (input.Length != BN254.G1EncodedLength * 2) + { + Assert.ThrowsExactly(() => CryptoLib.Bn254Add(input), vector.Name); + continue; + } + + byte[] expected = HexToBytes(vector.Expected); + byte[] actual = CryptoLib.Bn254Add(input); + CollectionAssert.AreEqual(expected, actual, vector.Name); + } + } + + [TestMethod] + public void TestBn254MulGethVectors() + { + foreach (var vector in LoadGethPrecompileVectors("bn256ScalarMul.json")) + { + byte[] input = HexToBytes(vector.Input); + if (input.Length != BN254.G1EncodedLength + BN254.FieldElementLength) + { + Assert.ThrowsExactly(() => CryptoLib.Bn254Mul(input), vector.Name); + continue; + } + + byte[] expected = HexToBytes(vector.Expected); + byte[] actual = CryptoLib.Bn254Mul(input); + CollectionAssert.AreEqual(expected, actual, vector.Name); + } + } + + [TestMethod] + public void TestBn254PairingGethVectors() + { + foreach (var vector in LoadGethPrecompileVectors("bn256Pairing.json")) + { + byte[] input = HexToBytes(vector.Input); + if (input.Length % BN254.PairInputLength != 0) + { + Assert.ThrowsExactly(() => CryptoLib.Bn254Pairing(input), vector.Name); + continue; + } + + byte[] expected = HexToBytes(vector.Expected); + byte[] actual = CryptoLib.Bn254Pairing(input); + CollectionAssert.AreEqual(expected, actual, vector.Name); + } + } + private bool CallVerifyWithEd25519(byte[] message, byte[] publicKey, byte[] signature) { var snapshot = TestBlockchain.GetTestSnapshotCache(); @@ -1174,5 +1343,52 @@ 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); + } + + private static IEnumerable LoadGethPrecompileVectors(string fileName) + { + var path = Path.Combine(AppContext.BaseDirectory, "SmartContract", "Native", "BN254TestVectors", fileName); + if (!File.Exists(path)) + throw new FileNotFoundException($"BN254 test vector file not found: {path}", path); + + using var stream = File.OpenRead(path); + var vectors = JsonSerializer.Deserialize>(stream, s_jsonOptions); + if (vectors is null || vectors.Count == 0) + throw new InvalidOperationException($"Unable to read BN254 test vectors from {path}."); + return vectors; + } + + private sealed record GethPrecompileVector + { + public string Name { get; init; } = string.Empty; + public string Input { get; init; } = string.Empty; + public string Expected { get; init; } = string.Empty; + } + + private static readonly JsonSerializerOptions s_jsonOptions = new() + { + PropertyNameCaseInsensitive = true + }; } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 9297af0951..5993b85049 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -43,7 +43,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}}"""}, From 6b551be38cacd38fe8954fbbdc015899d6c96976 Mon Sep 17 00:00:00 2001 From: jimmy Date: Sat, 8 Nov 2025 01:13:04 +0800 Subject: [PATCH 2/9] fix format --- tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index 167c55ebef..4964115d7a 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -1254,8 +1254,8 @@ public void TestBn254PairingVectors() } else { - Assert.IsTrue(result.All(b => b == 0), label); - } + Assert.IsTrue(result.All(b => b == 0), label); + } } } From a08a87b53423e58d6f8466c95ffbda473590f6cb Mon Sep 17 00:00:00 2001 From: jimmy Date: Sat, 8 Nov 2025 01:20:09 +0800 Subject: [PATCH 3/9] Enable unsafe build for BN254 --- src/Neo/Neo.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj index 374ee2b65b..5310c78d0e 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -2,6 +2,7 @@ net10.0 + true NEO;AntShares;Blockchain;Smart Contract From 30603f9489e6ba82df1ab30a79347887b8305433 Mon Sep 17 00:00:00 2001 From: jimmy Date: Sat, 8 Nov 2025 01:25:20 +0800 Subject: [PATCH 4/9] Document BN254 native methods --- docs/native-contracts-api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/native-contracts-api.md b/docs/native-contracts-api.md index d89d261bf3..dc69b5df0e 100644 --- a/docs/native-contracts-api.md +++ b/docs/native-contracts-api.md @@ -79,6 +79,9 @@ When calling a native contract method by transaction script, there are several t | bls12381Add | Add operation of two points. | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | -- | | bls12381Mul | Mul operation of gt point and multiplier | InteropInterface(*x*), Byte[](*mul*), Boolean(*neg*) | InteropInterface | 1<<21 | 0 | -- | -- | | bls12381Pairing | Pairing operation of g1 and g2 | InteropInterface(*g1*), InteropInterface(*g2*) | InteropInterface | 1<<23 | 0 | -- | -- | +| bn254Add | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | +| bn254Mul | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | +| bn254Pairing | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | | recoverSecp256K1 | Recovers the public key from a secp256k1 signature in a single byte array format. | Byte[](*messageHash*), Byte[](*signature*) | Byte[] | 1<<15 | 0 | -- | HF_Echidna | | ripemd160 | Computes the hash value for the specified byte array using the ripemd160 algorithm. | Byte[](*data*) | Byte[] | 1<<15 | 0 | -- | -- | | sha256 | Computes the hash value for the specified byte array using the sha256 algorithm. | Byte[](*data*) | Byte[] | 1<<15 | 0 | -- | -- | From 0eee6d8f6ec570fff37a823c875af353674f911b Mon Sep 17 00:00:00 2001 From: jimmy Date: Mon, 10 Nov 2025 20:05:36 +0800 Subject: [PATCH 5/9] Reduce BN254 pairing final exponentiation overhead --- src/Neo/Cryptography/BN254.cs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Neo/Cryptography/BN254.cs b/src/Neo/Cryptography/BN254.cs index d80a521239..b965c8ca6d 100644 --- a/src/Neo/Cryptography/BN254.cs +++ b/src/Neo/Cryptography/BN254.cs @@ -80,7 +80,6 @@ public static byte[] Pairing(ReadOnlySpan input) bool hasEffectivePair = false; mclBnGT accumulator = default; - Mcl.mclBnGT_setInt32(ref accumulator, 1); for (int pairIndex = 0; pairIndex < pairCount; pairIndex++) { @@ -97,21 +96,26 @@ public static byte[] Pairing(ReadOnlySpan input) 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); + // Accumulate Miller loops so we only run the final exponent once. + if (!hasEffectivePair) + { + Mcl.mclBn_millerLoop(ref accumulator, g1, g2); + hasEffectivePair = true; + } + else + { + mclBnGT loop = default; + Mcl.mclBn_millerLoop(ref loop, g1, g2); + mclBnGT temp = accumulator; + Mcl.mclBnGT_mul(ref accumulator, temp, loop); + } } if (!hasEffectivePair) return SuccessWord(); + Mcl.mclBn_finalExp(ref accumulator, accumulator); + return Mcl.mclBnGT_isOne(accumulator) == 1 ? SuccessWord() : new byte[FieldElementLength]; } From 35835aec71707eca2c588398d5727902cab0675d Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 10 Nov 2025 21:48:42 +0800 Subject: [PATCH 6/9] Apply suggestions from code review Co-authored-by: Anna Shaleva --- src/Neo/SmartContract/Native/CryptoLib.BN254.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs index e54959a4c4..ac63c76efe 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs @@ -24,7 +24,7 @@ public static byte[] Bn254Add(byte[] input) return BN254.Add(input); } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] public static byte[] Bn254Mul(byte[] input) { ArgumentNullException.ThrowIfNull(input); From 43799ea4f5bd702fdc4916d79d5af79b87297629 Mon Sep 17 00:00:00 2001 From: jimmy Date: Mon, 10 Nov 2025 22:15:35 +0800 Subject: [PATCH 7/9] Refine BN254 CryptoLib API and tests --- docs/native-contracts-api.md | 11 +- src/Neo/Cryptography/BN254.cs | 67 ++++++-- .../SmartContract/Native/CryptoLib.BN254.cs | 158 +++++++++++++++++- .../SmartContract/Native/UT_CryptoLib.cs | 102 ++++++----- .../SmartContract/Native/UT_NativeContract.cs | 2 +- 5 files changed, 276 insertions(+), 64 deletions(-) diff --git a/docs/native-contracts-api.md b/docs/native-contracts-api.md index dc69b5df0e..974f5b060c 100644 --- a/docs/native-contracts-api.md +++ b/docs/native-contracts-api.md @@ -79,9 +79,14 @@ When calling a native contract method by transaction script, there are several t | bls12381Add | Add operation of two points. | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | -- | | bls12381Mul | Mul operation of gt point and multiplier | InteropInterface(*x*), Byte[](*mul*), Boolean(*neg*) | InteropInterface | 1<<21 | 0 | -- | -- | | bls12381Pairing | Pairing operation of g1 and g2 | InteropInterface(*g1*), InteropInterface(*g2*) | InteropInterface | 1<<23 | 0 | -- | -- | -| bn254Add | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | -| bn254Mul | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | -| bn254Pairing | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | +| bn254Serialize | -- | InteropInterface(*point*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | +| bn254Deserialize | -- | Byte[](*data*) | InteropInterface | 1<<19 | 0 | -- | HF_Gorgon | +| bn254Add | -- | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | HF_Gorgon | +| bn254Mul | -- | InteropInterface(*point*), Byte[](*scalar*) | InteropInterface | 1<<21 | 0 | -- | HF_Gorgon | +| bn254Pairing | -- | Array(*pairs*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | +| bn254_add | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | +| bn254_mul | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | +| bn254_pairing | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | | recoverSecp256K1 | Recovers the public key from a secp256k1 signature in a single byte array format. | Byte[](*messageHash*), Byte[](*signature*) | Byte[] | 1<<15 | 0 | -- | HF_Echidna | | ripemd160 | Computes the hash value for the specified byte array using the ripemd160 algorithm. | Byte[](*data*) | Byte[] | 1<<15 | 0 | -- | -- | | sha256 | Computes the hash value for the specified byte array using the sha256 algorithm. | Byte[](*data*) | Byte[] | 1<<15 | 0 | -- | -- | diff --git a/src/Neo/Cryptography/BN254.cs b/src/Neo/Cryptography/BN254.cs index b965c8ca6d..992509f5df 100644 --- a/src/Neo/Cryptography/BN254.cs +++ b/src/Neo/Cryptography/BN254.cs @@ -21,7 +21,8 @@ public static class BN254 { public const int FieldElementLength = 32; public const int G1EncodedLength = 64; - public const int PairInputLength = 192; + public const int G2EncodedLength = FieldElementLength * 4; + public const int PairInputLength = G1EncodedLength + G2EncodedLength; private static readonly object s_sync = new(); private static bool s_initialized; @@ -68,24 +69,23 @@ public static byte[] Mul(ReadOnlySpan input) public static byte[] Pairing(ReadOnlySpan input) { - if (input.Length % PairInputLength != 0) - throw new ArgumentException("Invalid BN254 pairing input length", nameof(input)); - EnsureInitialized(); if (input.Length == 0) return SuccessWord(); + if (input.Length % PairInputLength != 0) + throw new ArgumentException("Invalid BN254 pairing input length", nameof(input)); + int pairCount = input.Length / PairInputLength; bool hasEffectivePair = false; - mclBnGT accumulator = default; 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); + var g2Slice = input.Slice(offset + G1EncodedLength, G2EncodedLength); if (!TryDeserializeG1(g1Slice, out var g1)) return new byte[FieldElementLength]; @@ -96,7 +96,6 @@ public static byte[] Pairing(ReadOnlySpan input) if (Mcl.mclBnG1_isZero(g1) == 1 || Mcl.mclBnG2_isZero(g2) == 1) continue; - // Accumulate Miller loops so we only run the final exponent once. if (!hasEffectivePair) { Mcl.mclBn_millerLoop(ref accumulator, g1, g2); @@ -119,7 +118,7 @@ public static byte[] Pairing(ReadOnlySpan input) return Mcl.mclBnGT_isOne(accumulator) == 1 ? SuccessWord() : new byte[FieldElementLength]; } - private static unsafe bool TryDeserializeG1(ReadOnlySpan encoded, out mclBnG1 point) + internal static unsafe bool TryDeserializeG1(ReadOnlySpan encoded, out mclBnG1 point) { point = default; @@ -145,7 +144,7 @@ private static unsafe bool TryDeserializeG1(ReadOnlySpan encoded, out mclB return Mcl.mclBnG1_isValid(point) == 1; } - private static unsafe bool TryDeserializeScalar(ReadOnlySpan encoded, out mclBnFr scalar) + internal static unsafe bool TryDeserializeScalar(ReadOnlySpan encoded, out mclBnFr scalar) { scalar = default; @@ -164,7 +163,7 @@ private static unsafe bool TryDeserializeScalar(ReadOnlySpan encoded, out return Mcl.mclBnFr_isValid(scalar) == 1; } - private static unsafe bool TryDeserializeG2(ReadOnlySpan encoded, out mclBnG2 point) + internal static unsafe bool TryDeserializeG2(ReadOnlySpan encoded, out mclBnG2 point) { point = default; @@ -210,7 +209,7 @@ private static unsafe bool TryDeserializeG2(ReadOnlySpan encoded, out mclB return true; } - private static unsafe byte[] SerializeG1(in mclBnG1 point) + internal static unsafe byte[] SerializeG1(in mclBnG1 point) { var output = new byte[G1EncodedLength]; @@ -238,6 +237,50 @@ private static unsafe byte[] SerializeG1(in mclBnG1 point) return output; } + internal static unsafe byte[] SerializeG2(in mclBnG2 point) + { + var output = new byte[G2EncodedLength]; + + if (Mcl.mclBnG2_isZero(point) == 1) + return output; + + Span scratch = stackalloc byte[FieldElementLength]; + + // x imaginary + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_getLittleEndian((nint)ptr, (nuint)scratch.Length, point.x.d1) == UIntPtr.Zero) + throw new ArgumentException("Failed to serialize BN254 point"); + } + WriteBigEndian(scratch, output.AsSpan(0, FieldElementLength)); + + // x real + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_getLittleEndian((nint)ptr, (nuint)scratch.Length, point.x.d0) == UIntPtr.Zero) + throw new ArgumentException("Failed to serialize BN254 point"); + } + WriteBigEndian(scratch, output.AsSpan(FieldElementLength, FieldElementLength)); + + // y imaginary + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_getLittleEndian((nint)ptr, (nuint)scratch.Length, point.y.d1) == UIntPtr.Zero) + throw new ArgumentException("Failed to serialize BN254 point"); + } + WriteBigEndian(scratch, output.AsSpan(FieldElementLength * 2, FieldElementLength)); + + // y real + fixed (byte* ptr = scratch) + { + if (Mcl.mclBnFp_getLittleEndian((nint)ptr, (nuint)scratch.Length, point.y.d0) == UIntPtr.Zero) + throw new ArgumentException("Failed to serialize BN254 point"); + } + WriteBigEndian(scratch, output.AsSpan(FieldElementLength * 3, FieldElementLength)); + + return output; + } + private static byte[] SuccessWord() { var output = new byte[FieldElementLength]; @@ -258,7 +301,7 @@ private static void CopyReversed(ReadOnlySpan source, Span destinati } [MethodImpl(MethodImplOptions.NoInlining)] - private static void EnsureInitialized() + internal static void EnsureInitialized() { if (s_initialized) return; diff --git a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs index ac63c76efe..65c51fc22c 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs @@ -10,34 +10,178 @@ // modifications are permitted. using Neo.Cryptography; +using Neo.VM.Types; +using Nethermind.MclBindings; using System; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { partial class CryptoLib { [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] - public static byte[] Bn254Add(byte[] input) + public static byte[] Bn254Serialize(InteropInterface point) { - ArgumentNullException.ThrowIfNull(input); + return point.GetInterface() switch + { + Bn254G1 g1 => g1.ToArray(), + Bn254G2 g2 => g2.ToArray(), + _ => throw new ArgumentException("BN254 type mismatch") + }; + } - return BN254.Add(input); + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + public static InteropInterface Bn254Deserialize(byte[] data) + { + ArgumentNullException.ThrowIfNull(data); + BN254.EnsureInitialized(); + return data.Length switch + { + BN254.G1EncodedLength when BN254.TryDeserializeG1(data, out _) => new InteropInterface(new Bn254G1(data)), + BN254.G2EncodedLength when BN254.TryDeserializeG2(data, out _) => new InteropInterface(new Bn254G2(data)), + _ => throw new ArgumentException("Invalid BN254 point length", nameof(data)) + }; + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + public static InteropInterface Bn254Add(InteropInterface x, InteropInterface y) + { + var pointX = GetBn254G1(x); + var pointY = GetBn254G1(y); + + if (!pointX.TryGetPoint(out var g1x) || !pointY.TryGetPoint(out var g1y)) + throw new ArgumentException("Invalid BN254 point data"); + + mclBnG1 result = default; + Mcl.mclBnG1_add(ref result, g1x, g1y); + Mcl.mclBnG1_normalize(ref result, result); + + return new InteropInterface(new Bn254G1(BN254.SerializeG1(result))); } [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] - public static byte[] Bn254Mul(byte[] input) + public static InteropInterface Bn254Mul(InteropInterface point, byte[] scalar) { - ArgumentNullException.ThrowIfNull(input); + ArgumentNullException.ThrowIfNull(scalar); + if (!BN254.TryDeserializeScalar(scalar, out var mul)) + throw new ArgumentException("Invalid BN254 scalar", nameof(scalar)); - return BN254.Mul(input); + var source = GetBn254G1(point); + if (!source.TryGetPoint(out var g1)) + throw new ArgumentException("Invalid BN254 point data"); + + mclBnG1 result = default; + Mcl.mclBnG1_mul(ref result, g1, mul); + Mcl.mclBnG1_normalize(ref result, result); + + return new InteropInterface(new Bn254G1(BN254.SerializeG1(result))); } [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] - public static byte[] Bn254Pairing(byte[] input) + public static byte[] Bn254Pairing(Array pairs) + { + ArgumentNullException.ThrowIfNull(pairs); + + if (pairs.Count == 0) + return BN254.Pairing(System.Array.Empty()); + + byte[] buffer = new byte[pairs.Count * BN254.PairInputLength]; + for (int i = 0; i < pairs.Count; i++) + { + if (pairs[i] is not Array pair || pair.Count != 2) + throw new ArgumentException("BN254 pairing pairs must contain g1 and g2 points"); + + if (pair[0] is not InteropInterface g1Interface) + throw new ArgumentException("BN254 pairing requires interop points"); + if (pair[1] is not InteropInterface g2Interface) + throw new ArgumentException("BN254 pairing requires interop points"); + + var g1 = GetBn254G1(g1Interface); + var g2 = GetBn254G2(g2Interface); + + var g1Bytes = g1.Encoded; + var g2Bytes = g2.Encoded; + var slice = buffer.AsSpan(i * BN254.PairInputLength, BN254.PairInputLength); + g1Bytes.CopyTo(slice[..g1Bytes.Length]); + g2Bytes.CopyTo(slice[g1Bytes.Length..]); + } + + return BN254.Pairing(buffer); + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19, Name = "bn254_add")] + public static byte[] Bn254AddRaw(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); + return BN254.Add(input); + } + + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21, Name = "bn254_mul")] + public static byte[] Bn254MulRaw(byte[] input) { ArgumentNullException.ThrowIfNull(input); + return BN254.Mul(input); + } + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21, Name = "bn254_pairing")] + public static byte[] Bn254PairingRaw(byte[] input) + { + ArgumentNullException.ThrowIfNull(input); return BN254.Pairing(input); } + + private static Bn254G1 GetBn254G1(InteropInterface item) + { + if (item.GetInterface() is not Bn254G1 point) + throw new ArgumentException("BN254 type mismatch"); + return point; + } + + private static Bn254G2 GetBn254G2(InteropInterface item) + { + if (item.GetInterface() is not Bn254G2 point) + throw new ArgumentException("BN254 type mismatch"); + return point; + } + + private sealed class Bn254G1 + { + private readonly byte[] _encoded; + + public Bn254G1(ReadOnlySpan encoded) + { + _encoded = encoded.ToArray(); + } + + public ReadOnlySpan Encoded => _encoded; + + public bool TryGetPoint(out mclBnG1 point) + { + BN254.EnsureInitialized(); + return BN254.TryDeserializeG1(Encoded, out point); + } + + public byte[] ToArray() => (byte[])_encoded.Clone(); + } + + private sealed class Bn254G2 + { + private readonly byte[] _encoded; + + public Bn254G2(ReadOnlySpan encoded) + { + _encoded = encoded.ToArray(); + } + + public ReadOnlySpan Encoded => _encoded; + + public bool TryGetPoint(out mclBnG2 point) + { + BN254.EnsureInitialized(); + return BN254.TryDeserializeG2(Encoded, out point); + } + + public byte[] ToArray() => (byte[])_encoded.Clone(); + } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index 4964115d7a..ab33961c78 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -20,6 +20,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; +using Neo.VM.Types; using Org.BouncyCastle.Utilities.Encoders; using System; using System.Collections.Generic; @@ -28,6 +29,7 @@ using System.Text; using System.Text.Json; using JsonSerializer = System.Text.Json.JsonSerializer; +using VMArray = Neo.VM.Types.Array; namespace Neo.UnitTests.SmartContract.Native { @@ -1143,7 +1145,7 @@ public void TestVerifyWithEd25519() { // byte[] privateKey = "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60".HexToBytes(); byte[] publicKey = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".HexToBytes(); - byte[] message = Array.Empty(); + byte[] message = System.Array.Empty(); byte[] signature = ("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" + "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b").HexToBytes(); @@ -1159,13 +1161,13 @@ public void TestVerifyWithEd25519() // Test with an invalid signature byte[] invalidSignature = new byte[signature.Length]; - Array.Copy(signature, invalidSignature, signature.Length); + System.Array.Copy(signature, invalidSignature, signature.Length); invalidSignature[0] ^= 0x01; // Flip one bit Assert.IsFalse(CallVerifyWithEd25519(message, publicKey, invalidSignature)); // Test with an invalid public key byte[] invalidPublicKey = new byte[publicKey.Length]; - Array.Copy(publicKey, invalidPublicKey, publicKey.Length); + System.Array.Copy(publicKey, invalidPublicKey, publicKey.Length); invalidPublicKey[0] ^= 0x01; // Flip one bit Assert.IsFalse(CallVerifyWithEd25519(message, invalidPublicKey, signature)); } @@ -1173,50 +1175,37 @@ public void TestVerifyWithEd25519() [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); - + var point = DecodeBn254G1(Bn254G1X, Bn254G1Y); + var result = CryptoLib.Bn254Add(point, point); + var serialized = CryptoLib.Bn254Serialize(result); byte[] expected = new byte[64]; WriteBn254Field(Bn254DoubleX, expected, 0); WriteBn254Field(Bn254DoubleY, expected, 32); - CollectionAssert.AreEqual(expected, result); + CollectionAssert.AreEqual(expected, serialized); } [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); - + var point = DecodeBn254G1(Bn254G1X, Bn254G1Y); + byte[] scalar = Bn254Field("2"); + var result = CryptoLib.Bn254Mul(point, scalar); + var serialized = CryptoLib.Bn254Serialize(result); byte[] expected = new byte[64]; WriteBn254Field(Bn254DoubleX, expected, 0); WriteBn254Field(Bn254DoubleY, expected, 32); - CollectionAssert.AreEqual(expected, result); + CollectionAssert.AreEqual(expected, serialized); } [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); + var g1Point = DecodeBn254G1(Bn254G1X, Bn254G1Y); + var g2Point = DecodeBn254G2(Bn254G2XIm, Bn254G2XRe, Bn254G2YIm, Bn254G2YRe); + var pairs = BuildBn254PairArray(new[] { (g1Point, g2Point) }); + byte[] result = CryptoLib.Bn254Pairing(pairs); Assert.IsTrue(result.All(b => b == 0)); } @@ -1224,7 +1213,7 @@ public void TestBn254PairingGenerator() [TestMethod] public void TestBn254PairingEmpty() { - byte[] result = CryptoLib.Bn254Pairing(Array.Empty()); + byte[] result = CryptoLib.Bn254Pairing(new VMArray()); Assert.IsTrue(result.Take(result.Length - 1).All(b => b == 0)); Assert.AreEqual(1, result[^1]); } @@ -1244,7 +1233,7 @@ public void TestBn254PairingVectors() foreach (var (hex, expectedSuccess, label) in cases) { byte[] input = HexToBytes(hex); - byte[] result = CryptoLib.Bn254Pairing(input); + byte[] result = CryptoLib.Bn254PairingRaw(input); Assert.AreEqual(32, result.Length, label); if (expectedSuccess) @@ -1262,9 +1251,9 @@ public void TestBn254PairingVectors() [TestMethod] public void TestBn254InvalidInputs() { - Assert.ThrowsExactly(() => CryptoLib.Bn254Add(Array.Empty())); - Assert.ThrowsExactly(() => CryptoLib.Bn254Mul(Array.Empty())); - Assert.ThrowsExactly(() => CryptoLib.Bn254Pairing(new byte[1])); + Assert.ThrowsExactly(() => CryptoLib.Bn254AddRaw(System.Array.Empty())); + Assert.ThrowsExactly(() => CryptoLib.Bn254MulRaw(System.Array.Empty())); + Assert.ThrowsExactly(() => CryptoLib.Bn254PairingRaw(new byte[1])); } [TestMethod] @@ -1275,12 +1264,12 @@ public void TestBn254AddGethVectors() byte[] input = HexToBytes(vector.Input); if (input.Length != BN254.G1EncodedLength * 2) { - Assert.ThrowsExactly(() => CryptoLib.Bn254Add(input), vector.Name); + Assert.ThrowsExactly(() => CryptoLib.Bn254AddRaw(input), vector.Name); continue; } byte[] expected = HexToBytes(vector.Expected); - byte[] actual = CryptoLib.Bn254Add(input); + byte[] actual = CryptoLib.Bn254AddRaw(input); CollectionAssert.AreEqual(expected, actual, vector.Name); } } @@ -1293,12 +1282,12 @@ public void TestBn254MulGethVectors() byte[] input = HexToBytes(vector.Input); if (input.Length != BN254.G1EncodedLength + BN254.FieldElementLength) { - Assert.ThrowsExactly(() => CryptoLib.Bn254Mul(input), vector.Name); + Assert.ThrowsExactly(() => CryptoLib.Bn254MulRaw(input), vector.Name); continue; } byte[] expected = HexToBytes(vector.Expected); - byte[] actual = CryptoLib.Bn254Mul(input); + byte[] actual = CryptoLib.Bn254MulRaw(input); CollectionAssert.AreEqual(expected, actual, vector.Name); } } @@ -1311,12 +1300,12 @@ public void TestBn254PairingGethVectors() byte[] input = HexToBytes(vector.Input); if (input.Length % BN254.PairInputLength != 0) { - Assert.ThrowsExactly(() => CryptoLib.Bn254Pairing(input), vector.Name); + Assert.ThrowsExactly(() => CryptoLib.Bn254PairingRaw(input), vector.Name); continue; } byte[] expected = HexToBytes(vector.Expected); - byte[] actual = CryptoLib.Bn254Pairing(input); + byte[] actual = CryptoLib.Bn254PairingRaw(input); CollectionAssert.AreEqual(expected, actual, vector.Name); } } @@ -1347,7 +1336,38 @@ private bool CallVerifyWithEd25519(byte[] message, byte[] publicKey, byte[] sign private static void WriteBn254Field(string hex, byte[] buffer, int offset) { var field = Bn254Field(hex); - Buffer.BlockCopy(field, 0, buffer, offset, field.Length); + System.Buffer.BlockCopy(field, 0, buffer, offset, field.Length); + } + + private static InteropInterface DecodeBn254G1(string x, string y) + { + byte[] encoded = new byte[BN254.G1EncodedLength]; + WriteBn254Field(x, encoded, 0); + WriteBn254Field(y, encoded, BN254.FieldElementLength); + return CryptoLib.Bn254Deserialize(encoded); + } + + private static InteropInterface DecodeBn254G2(string xImag, string xReal, string yImag, string yReal) + { + byte[] encoded = new byte[BN254.G2EncodedLength]; + WriteBn254Field(xImag, encoded, 0); + WriteBn254Field(xReal, encoded, BN254.FieldElementLength); + WriteBn254Field(yImag, encoded, BN254.FieldElementLength * 2); + WriteBn254Field(yReal, encoded, BN254.FieldElementLength * 3); + return CryptoLib.Bn254Deserialize(encoded); + } + + private static VMArray BuildBn254PairArray(IEnumerable<(InteropInterface G1, InteropInterface G2)> pairs) + { + VMArray array = new(); + foreach (var (g1, g2) in pairs) + { + VMArray pair = new(); + pair.Add(g1); + pair.Add(g2); + array.Add(pair); + } + return array; } private static byte[] Bn254Field(string hex) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 5993b85049..7a19451832 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -43,7 +43,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":"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}}"""}, + {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"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":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":42,"safe":true},{"name":"bn254Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":49,"safe":true},{"name":"bn254Mul","parameters":[{"name":"point","type":"InteropInterface"},{"name":"scalar","type":"ByteArray"}],"returntype":"InteropInterface","offset":56,"safe":true},{"name":"bn254Pairing","parameters":[{"name":"pairs","type":"Array"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"bn254Serialize","parameters":[{"name":"point","type":"InteropInterface"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"bn254_add","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":77,"safe":true},{"name":"bn254_mul","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":84,"safe":true},{"name":"bn254_pairing","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":91,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":98,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":105,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":112,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":119,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":126,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":133,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":140,"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}}"""}, From 33ecb69783dac0185451ebdc43f9e23d2cad6635 Mon Sep 17 00:00:00 2001 From: jimmy Date: Mon, 10 Nov 2025 22:29:08 +0800 Subject: [PATCH 8/9] Add bn254Equal and sync manifests --- docs/native-contracts-api.md | 1 + src/Neo/SmartContract/Native/CryptoLib.BN254.cs | 16 ++++++++++++++++ .../SmartContract/Native/UT_CryptoLib.cs | 15 +++++++++++++++ .../SmartContract/Native/UT_NativeContract.cs | 2 +- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/native-contracts-api.md b/docs/native-contracts-api.md index 974f5b060c..71771e637a 100644 --- a/docs/native-contracts-api.md +++ b/docs/native-contracts-api.md @@ -82,6 +82,7 @@ When calling a native contract method by transaction script, there are several t | bn254Serialize | -- | InteropInterface(*point*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | | bn254Deserialize | -- | Byte[](*data*) | InteropInterface | 1<<19 | 0 | -- | HF_Gorgon | | bn254Add | -- | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | HF_Gorgon | +| bn254Equal | -- | InteropInterface(*x*), InteropInterface(*y*) | Boolean | 1<<5 | 0 | -- | HF_Gorgon | | bn254Mul | -- | InteropInterface(*point*), Byte[](*scalar*) | InteropInterface | 1<<21 | 0 | -- | HF_Gorgon | | bn254Pairing | -- | Array(*pairs*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | | bn254_add | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | diff --git a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs index 65c51fc22c..00daf480bf 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs @@ -13,6 +13,7 @@ using Neo.VM.Types; using Nethermind.MclBindings; using System; +using System.Linq; using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native @@ -59,6 +60,17 @@ public static InteropInterface Bn254Add(InteropInterface x, InteropInterface y) return new InteropInterface(new Bn254G1(BN254.SerializeG1(result))); } + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 5)] + public static bool Bn254Equal(InteropInterface x, InteropInterface y) + { + return (x.GetInterface(), y.GetInterface()) switch + { + (Bn254G1 a, Bn254G1 b) => a.SequenceEqual(b), + (Bn254G2 a, Bn254G2 b) => a.SequenceEqual(b), + _ => throw new ArgumentException("BN254 type mismatch") + }; + } + [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] public static InteropInterface Bn254Mul(InteropInterface point, byte[] scalar) { @@ -162,6 +174,8 @@ public bool TryGetPoint(out mclBnG1 point) } public byte[] ToArray() => (byte[])_encoded.Clone(); + + public bool SequenceEqual(Bn254G1 other) => Encoded.SequenceEqual(other.Encoded); } private sealed class Bn254G2 @@ -182,6 +196,8 @@ public bool TryGetPoint(out mclBnG2 point) } public byte[] ToArray() => (byte[])_encoded.Clone(); + + public bool SequenceEqual(Bn254G2 other) => Encoded.SequenceEqual(other.Encoded); } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs index ab33961c78..511f97b34b 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs @@ -1199,6 +1199,21 @@ public void TestBn254Mul() CollectionAssert.AreEqual(expected, serialized); } + [TestMethod] + public void TestBn254Equal() + { + var point = DecodeBn254G1(Bn254G1X, Bn254G1Y); + var same = DecodeBn254G1(Bn254G1X, Bn254G1Y); + + Assert.IsTrue(CryptoLib.Bn254Equal(point, same)); + + var doubled = CryptoLib.Bn254Add(point, same); + Assert.IsFalse(CryptoLib.Bn254Equal(point, doubled)); + + var g2Point = DecodeBn254G2(Bn254G2XIm, Bn254G2XRe, Bn254G2YIm, Bn254G2YRe); + Assert.ThrowsExactly(() => CryptoLib.Bn254Equal(point, g2Point)); + } + [TestMethod] public void TestBn254PairingGenerator() { diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 7a19451832..88f3fd0b18 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -43,7 +43,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":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"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":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":42,"safe":true},{"name":"bn254Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":49,"safe":true},{"name":"bn254Mul","parameters":[{"name":"point","type":"InteropInterface"},{"name":"scalar","type":"ByteArray"}],"returntype":"InteropInterface","offset":56,"safe":true},{"name":"bn254Pairing","parameters":[{"name":"pairs","type":"Array"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"bn254Serialize","parameters":[{"name":"point","type":"InteropInterface"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"bn254_add","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":77,"safe":true},{"name":"bn254_mul","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":84,"safe":true},{"name":"bn254_pairing","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":91,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":98,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":105,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":112,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":119,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":126,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":133,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":140,"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":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":751055395},"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":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":42,"safe":true},{"name":"bn254Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":49,"safe":true},{"name":"bn254Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":56,"safe":true},{"name":"bn254Mul","parameters":[{"name":"point","type":"InteropInterface"},{"name":"scalar","type":"ByteArray"}],"returntype":"InteropInterface","offset":63,"safe":true},{"name":"bn254Pairing","parameters":[{"name":"pairs","type":"Array"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"bn254Serialize","parameters":[{"name":"point","type":"InteropInterface"}],"returntype":"ByteArray","offset":77,"safe":true},{"name":"bn254_add","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":84,"safe":true},{"name":"bn254_mul","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":91,"safe":true},{"name":"bn254_pairing","parameters":[{"name":"input","type":"ByteArray"}],"returntype":"ByteArray","offset":98,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":105,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":112,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":119,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":126,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":133,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":140,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":147,"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}}"""}, From 546a6d07afa3a7171891b1f761f5bb26e63528ab Mon Sep 17 00:00:00 2001 From: jimmy Date: Tue, 11 Nov 2025 09:43:40 +0800 Subject: [PATCH 9/9] Activate BN254 API in Faun hardfork --- docs/native-contracts-api.md | 18 +++++++++--------- .../SmartContract/Native/CryptoLib.BN254.cs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/native-contracts-api.md b/docs/native-contracts-api.md index 71771e637a..09a0f51501 100644 --- a/docs/native-contracts-api.md +++ b/docs/native-contracts-api.md @@ -79,15 +79,15 @@ When calling a native contract method by transaction script, there are several t | bls12381Add | Add operation of two points. | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | -- | | bls12381Mul | Mul operation of gt point and multiplier | InteropInterface(*x*), Byte[](*mul*), Boolean(*neg*) | InteropInterface | 1<<21 | 0 | -- | -- | | bls12381Pairing | Pairing operation of g1 and g2 | InteropInterface(*g1*), InteropInterface(*g2*) | InteropInterface | 1<<23 | 0 | -- | -- | -| bn254Serialize | -- | InteropInterface(*point*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | -| bn254Deserialize | -- | Byte[](*data*) | InteropInterface | 1<<19 | 0 | -- | HF_Gorgon | -| bn254Add | -- | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | HF_Gorgon | -| bn254Equal | -- | InteropInterface(*x*), InteropInterface(*y*) | Boolean | 1<<5 | 0 | -- | HF_Gorgon | -| bn254Mul | -- | InteropInterface(*point*), Byte[](*scalar*) | InteropInterface | 1<<21 | 0 | -- | HF_Gorgon | -| bn254Pairing | -- | Array(*pairs*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | -| bn254_add | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Gorgon | -| bn254_mul | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | -| bn254_pairing | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Gorgon | +| bn254Serialize | -- | InteropInterface(*point*) | Byte[] | 1<<19 | 0 | -- | HF_Faun | +| bn254Deserialize | -- | Byte[](*data*) | InteropInterface | 1<<19 | 0 | -- | HF_Faun | +| bn254Add | -- | InteropInterface(*x*), InteropInterface(*y*) | InteropInterface | 1<<19 | 0 | -- | HF_Faun | +| bn254Equal | -- | InteropInterface(*x*), InteropInterface(*y*) | Boolean | 1<<5 | 0 | -- | HF_Faun | +| bn254Mul | -- | InteropInterface(*point*), Byte[](*scalar*) | InteropInterface | 1<<21 | 0 | -- | HF_Faun | +| bn254Pairing | -- | Array(*pairs*) | Byte[] | 1<<21 | 0 | -- | HF_Faun | +| bn254_add | -- | Byte[](*input*) | Byte[] | 1<<19 | 0 | -- | HF_Faun | +| bn254_mul | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Faun | +| bn254_pairing | -- | Byte[](*input*) | Byte[] | 1<<21 | 0 | -- | HF_Faun | | recoverSecp256K1 | Recovers the public key from a secp256k1 signature in a single byte array format. | Byte[](*messageHash*), Byte[](*signature*) | Byte[] | 1<<15 | 0 | -- | HF_Echidna | | ripemd160 | Computes the hash value for the specified byte array using the ripemd160 algorithm. | Byte[](*data*) | Byte[] | 1<<15 | 0 | -- | -- | | sha256 | Computes the hash value for the specified byte array using the sha256 algorithm. | Byte[](*data*) | Byte[] | 1<<15 | 0 | -- | -- | diff --git a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs index 00daf480bf..ae7ea9d537 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.BN254.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.BN254.cs @@ -20,7 +20,7 @@ namespace Neo.SmartContract.Native { partial class CryptoLib { - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 19)] public static byte[] Bn254Serialize(InteropInterface point) { return point.GetInterface() switch @@ -31,7 +31,7 @@ public static byte[] Bn254Serialize(InteropInterface point) }; } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 19)] public static InteropInterface Bn254Deserialize(byte[] data) { ArgumentNullException.ThrowIfNull(data); @@ -44,7 +44,7 @@ public static InteropInterface Bn254Deserialize(byte[] data) }; } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 19)] public static InteropInterface Bn254Add(InteropInterface x, InteropInterface y) { var pointX = GetBn254G1(x); @@ -60,7 +60,7 @@ public static InteropInterface Bn254Add(InteropInterface x, InteropInterface y) return new InteropInterface(new Bn254G1(BN254.SerializeG1(result))); } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 5)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 5)] public static bool Bn254Equal(InteropInterface x, InteropInterface y) { return (x.GetInterface(), y.GetInterface()) switch @@ -71,7 +71,7 @@ public static bool Bn254Equal(InteropInterface x, InteropInterface y) }; } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 21)] public static InteropInterface Bn254Mul(InteropInterface point, byte[] scalar) { ArgumentNullException.ThrowIfNull(scalar); @@ -89,7 +89,7 @@ public static InteropInterface Bn254Mul(InteropInterface point, byte[] scalar) return new InteropInterface(new Bn254G1(BN254.SerializeG1(result))); } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 21)] public static byte[] Bn254Pairing(Array pairs) { ArgumentNullException.ThrowIfNull(pairs); @@ -121,21 +121,21 @@ public static byte[] Bn254Pairing(Array pairs) return BN254.Pairing(buffer); } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19, Name = "bn254_add")] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 19, Name = "bn254_add")] public static byte[] Bn254AddRaw(byte[] input) { ArgumentNullException.ThrowIfNull(input); return BN254.Add(input); } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21, Name = "bn254_mul")] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 21, Name = "bn254_mul")] public static byte[] Bn254MulRaw(byte[] input) { ArgumentNullException.ThrowIfNull(input); return BN254.Mul(input); } - [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 21, Name = "bn254_pairing")] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 21, Name = "bn254_pairing")] public static byte[] Bn254PairingRaw(byte[] input) { ArgumentNullException.ThrowIfNull(input);