diff --git a/.devcontainer/devcontainer.dockerfile b/.devcontainer/devcontainer.dockerfile index 3b16107e9e..158bc6132a 100644 --- a/.devcontainer/devcontainer.dockerfile +++ b/.devcontainer/devcontainer.dockerfile @@ -1,3 +1,3 @@ -FROM mcr.microsoft.com/devcontainers/dotnet:8.0-jammy +FROM mcr.microsoft.com/devcontainers/dotnet:9.0-jammy # Install the libleveldb-dev package RUN apt-get update && apt-get install -y libleveldb-dev diff --git a/.editorconfig b/.editorconfig index ccbd29fd03..af8e3350e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -55,6 +55,11 @@ indent_size = 2 # Dotnet code style settings: [*.{cs,vb}] +# Use block-scoped namespace +csharp_style_namespace_declarations = block_scoped:error +dotnet_diagnostic.IDE0160.severity = error +dotnet_diagnostic.IDE0161.severity = error + # Member can be made 'readonly' csharp_style_prefer_readonly_struct_member = true dotnet_diagnostic.IDE0251.severity = warning diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md index 9b98d24f99..ddc181fabb 100644 --- a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md +++ b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md @@ -2,7 +2,7 @@ name: Feature or enhancement request about: Suggest an idea for Neo title: '' -labels: discussion +labels: Discussion assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/questions.md b/.github/ISSUE_TEMPLATE/questions.md index 4d97925f89..4945492ae3 100644 --- a/.github/ISSUE_TEMPLATE/questions.md +++ b/.github/ISSUE_TEMPLATE/questions.md @@ -2,7 +2,7 @@ name: Questions about: Questions about Neo Platform title: '' -labels: question +labels: Question assignees: '' --- diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3d8e7d0d6c..4eb8ecb034 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,7 +5,7 @@ on: types: [published] env: - DOTNET_VERSION: 8.0.x + DOTNET_VERSION: 9.0.x DIST_DIR: ./dist jobs: @@ -29,7 +29,7 @@ jobs: - name: Build (neo-cli) run: | dotnet publish ./src/Neo.CLI \ - --framework net8.0 \ + --framework net9.0 \ --configuration Release \ --runtime linux-x64 \ --self-contained true \ @@ -49,7 +49,7 @@ jobs: - name: Build (LevelDbStore) run: | dotnet build ./src/Plugins/LevelDBStore \ - --framework net8.0 \ + --framework net9.0 \ --configuration Release \ --output ${{ env.DIST_DIR }}/Plugins/LevelDBStore \ --verbosity normal \ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 22cf4d08df..fd290a9111 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ on: pull_request: env: - DOTNET_VERSION: 8.0.x + DOTNET_VERSION: 9.0.x jobs: @@ -63,9 +63,17 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - - - name: Test - if: matrix.os != 'ubuntu-latest' + + - name: Test (MacOS) + if: matrix.os == 'macos-latest' + run: | + brew install leveldb + dotnet build + cp -vp /opt/homebrew/Cellar/leveldb/1.23_1/lib/libleveldb.dylib ./tests/Neo.Plugins.Storage.Tests/bin/Debug/net9.0/ + dotnet test --blame-hang --blame-crash --no-build + + - name: Test (windows) + if: matrix.os == 'windows-latest' run: | dotnet sln neo.sln remove ./tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj dotnet build diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 3fa6cc4f5e..cc0beff7df 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -7,7 +7,7 @@ on: # Define environment variables env: - DOTNET_VERSION: 8.0.x + DOTNET_VERSION: 9.0.x CONFIGURATION: Release jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bcfddc1185..b1d0107825 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: # Define environment variables env: - DOTNET_VERSION: 8.0.x + DOTNET_VERSION: 9.0.x CONFIGURATION: Release DIST_PATH: /tmp/dist OUTPUT_PATH: /tmp/out @@ -106,7 +106,7 @@ jobs: run: | dotnet publish ./src/Neo.CLI \ --version-suffix ${{ matrix.runtime }} \ - --framework net8.0 \ + --framework net9.0 \ --configuration ${{ env.CONFIGURATION }} \ --runtime ${{ matrix.runtime }} \ --self-contained true \ @@ -128,7 +128,7 @@ jobs: run: | dotnet build ./src/Plugins/LevelDBStore \ --version-suffix ${{ matrix.runtime }} \ - --framework net8.0 \ + --framework net9.0 \ --configuration ${{ env.CONFIGURATION }} \ --output ${{ env.OUTPUT_PATH }}/${{ matrix.runtime }}/Plugins/LevelDBStore \ --verbosity normal \ diff --git a/.gitignore b/.gitignore index 45ccb8c40a..16342efe06 100644 --- a/.gitignore +++ b/.gitignore @@ -257,6 +257,8 @@ paket-files/ PublishProfiles /.vscode launchSettings.json +/coverages +**/.DS_Store # Benchmarks -**/BenchmarkDotNet.Artifacts/ \ No newline at end of file +**/BenchmarkDotNet.Artifacts/ diff --git a/README.md b/README.md index cc1c9b384a..e7f4e66eea 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

A modern distributed network for the Smart Economy.
- Documentation » + Documentation »

Neo @@ -106,7 +106,7 @@ ## Overview This repository contain main classes of the [Neo](https://neo.org) blockchain. -Visit the [tutorials](https://developers.neo.org) to get started. +Visit the [tutorials](https://docs.neo.org) to get started. ## Project structure diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs b/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs index a63542bace..ce2fd99a83 100644 --- a/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs +++ b/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs @@ -16,41 +16,42 @@ using System.IO.Hashing; using System.Text; -namespace Neo.Benchmark; - -public class Benchmarks_Hash +namespace Neo.Benchmark { - // 256 KiB - static readonly byte[] data = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat("Hello, World!^_^", 16 * 1024))); - - static readonly byte[] hash = "9182abedfbb9b18d81a05d8bcb45489e7daa2858".HexToBytes(); - - [Benchmark] - public void RIPEMD160_ComputeHash() - { - using var ripemd160 = new RIPEMD160Managed(); - var result = ripemd160.ComputeHash(data); - Debug.Assert(result.SequenceEqual(hash)); - } - - [Benchmark] - public void XxHash32_HashToUInt32() - { - var result = XxHash32.HashToUInt32(data); - Debug.Assert(result == 682967318u); - } - - [Benchmark] - public void XxHash3_HashToUInt64() - { - var result = (uint)XxHash3.HashToUInt64(data); - Debug.Assert(result == 1389469485u); - } - - [Benchmark] - public void Murmur32_HashToUInt32() + public class Benchmarks_Hash { - var result = data.Murmur32(0); - Debug.Assert(result == 3731881930u); + // 256 KiB + static readonly byte[] data = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat("Hello, World!^_^", 16 * 1024))); + + static readonly byte[] hash = "9182abedfbb9b18d81a05d8bcb45489e7daa2858".HexToBytes(); + + [Benchmark] + public void RIPEMD160_ComputeHash() + { + using var ripemd160 = new RIPEMD160Managed(); + var result = ripemd160.ComputeHash(data); + Debug.Assert(result.SequenceEqual(hash)); + } + + [Benchmark] + public void XxHash32_HashToUInt32() + { + var result = XxHash32.HashToUInt32(data); + Debug.Assert(result == 682967318u); + } + + [Benchmark] + public void XxHash3_HashToUInt64() + { + var result = (uint)XxHash3.HashToUInt64(data); + Debug.Assert(result == 1389469485u); + } + + [Benchmark] + public void Murmur32_HashToUInt32() + { + var result = data.Murmur32(0); + Debug.Assert(result == 3731881930u); + } } } diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.POC.cs b/benchmarks/Neo.Benchmarks/Benchmarks.POC.cs index f073c543a9..00fbf8d1f0 100644 --- a/benchmarks/Neo.Benchmarks/Benchmarks.POC.cs +++ b/benchmarks/Neo.Benchmarks/Benchmarks.POC.cs @@ -15,64 +15,65 @@ using Neo.VM; using System.Diagnostics; -namespace Neo.Benchmark; - -public class Benchmarks_PoCs +namespace Neo.Benchmark { - private static readonly ProtocolSettings protocol = ProtocolSettings.Load("config.json"); - private static readonly NeoSystem system = new(protocol, (string)null); - - [Benchmark] - public void NeoIssue2725() + public class Benchmarks_PoCs { - // https://github.com/neo-project/neo/issues/2725 - // L00: INITSSLOT 1 - // L01: NEWARRAY0 - // L02: PUSHDATA1 6161616161 //"aaaaa" - // L03: PUSHINT16 500 - // L04: STSFLD0 - // L05: OVER - // L06: OVER - // L07: SYSCALL 95016f61 //System.Runtime.Notify - // L08: LDSFLD0 - // L09: DEC - // L10: DUP - // L11: STSFLD0 - // L12: JMPIF L05 - // L13: CLEAR - // L14: SYSCALL dbfea874 //System.Runtime.GetExecutingScriptHash - // L15: PUSHINT16 8000 - // L16: STSFLD0 - // L17: DUP - // L18: SYSCALL 274335f1 //System.Runtime.GetNotifications - // L19: DROP - // L20: LDSFLD0 - // L21: DEC - // L22: DUP - // L23: STSFLD0 - // L24: JMPIF L17 - Run(nameof(NeoIssue2725), "VgHCDAVhYWFhYQH0AWBLS0GVAW9hWJ1KYCT1SUHb/qh0AUAfYEpBJ0M18UVYnUpgJPU="); - } + private static readonly ProtocolSettings protocol = ProtocolSettings.Load("config.json"); + private static readonly NeoSystem system = new(protocol, (string)null); - private static void Run(string name, string poc) - { - Random random = new(); - Transaction tx = new() + [Benchmark] + public void NeoIssue2725() + { + // https://github.com/neo-project/neo/issues/2725 + // L00: INITSSLOT 1 + // L01: NEWARRAY0 + // L02: PUSHDATA1 6161616161 //"aaaaa" + // L03: PUSHINT16 500 + // L04: STSFLD0 + // L05: OVER + // L06: OVER + // L07: SYSCALL 95016f61 //System.Runtime.Notify + // L08: LDSFLD0 + // L09: DEC + // L10: DUP + // L11: STSFLD0 + // L12: JMPIF L05 + // L13: CLEAR + // L14: SYSCALL dbfea874 //System.Runtime.GetExecutingScriptHash + // L15: PUSHINT16 8000 + // L16: STSFLD0 + // L17: DUP + // L18: SYSCALL 274335f1 //System.Runtime.GetNotifications + // L19: DROP + // L20: LDSFLD0 + // L21: DEC + // L22: DUP + // L23: STSFLD0 + // L24: JMPIF L17 + Run(nameof(NeoIssue2725), "VgHCDAVhYWFhYQH0AWBLS0GVAW9hWJ1KYCT1SUHb/qh0AUAfYEpBJ0M18UVYnUpgJPU="); + } + + private static void Run(string name, string poc) { - Version = 0, - Nonce = (uint)random.Next(), - SystemFee = 20_00000000, - NetworkFee = 1_00000000, - ValidUntilBlock = ProtocolSettings.Default.MaxTraceableBlocks, - Signers = Array.Empty(), - Attributes = Array.Empty(), - Script = Convert.FromBase64String(poc), - Witnesses = Array.Empty() - }; - using var snapshot = system.GetSnapshotCache(); - using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, system.GenesisBlock, protocol, tx.SystemFee); - engine.LoadScript(tx.Script); - engine.Execute(); - Debug.Assert(engine.State == VMState.FAULT); + Random random = new(); + Transaction tx = new() + { + Version = 0, + Nonce = (uint)random.Next(), + SystemFee = 20_00000000, + NetworkFee = 1_00000000, + ValidUntilBlock = ProtocolSettings.Default.MaxTraceableBlocks, + Signers = Array.Empty(), + Attributes = Array.Empty(), + Script = Convert.FromBase64String(poc), + Witnesses = Array.Empty() + }; + using var snapshot = system.GetSnapshotCache(); + using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, system.GenesisBlock, protocol, tx.SystemFee); + engine.LoadScript(tx.Script); + engine.Execute(); + Debug.Assert(engine.State == VMState.FAULT); + } } } diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs index 37fa701ff9..3f46539d75 100644 --- a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs +++ b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs @@ -11,146 +11,147 @@ using BenchmarkDotNet.Attributes; -namespace Neo.Benchmark; - -public class Benchmarks_UInt160 +namespace Neo.Benchmark { - static readonly OldUInt160 s_oldUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - static readonly UInt160 s_newUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - - [Benchmark] - public void TestOldUInt160Gernerator1() - { - _ = new OldUInt160(); - } - - [Benchmark] - public void TestOldUInt160Gernerator2() - { - _ = new OldUInt160(new byte[20]); - } - - [Benchmark] - public void TestOldUInt160CompareTo() - { - OldUInt160.Zero.CompareTo(OldUInt160.Zero); - OldUInt160.Zero.CompareTo(s_oldUInt160); - s_oldUInt160.CompareTo(OldUInt160.Zero); - } - - [Benchmark] - public void TestOldUInt160Equals() - { - OldUInt160.Zero.Equals(OldUInt160.Zero); - OldUInt160.Zero.Equals(s_oldUInt160); - s_oldUInt160.Equals(null); - } - - [Benchmark] - public void TestOldUInt160Parse() - { - _ = OldUInt160.Parse("0x0000000000000000000000000000000000000000"); - _ = OldUInt160.Parse("0000000000000000000000000000000000000000"); - } - - [Benchmark] - public void TestOldUInt160TryParse() - { - OldUInt160.TryParse(null, out _); - OldUInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); - OldUInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); - OldUInt160.TryParse("000000000000000000000000000000000000000", out _); - } - - [Benchmark] - public void TestOldUInt160OperatorLarger() - { - _ = s_oldUInt160 > OldUInt160.Zero; - } - - [Benchmark] - public void TestOldUInt160OperatorLargerAndEqual() - { - _ = s_oldUInt160 >= OldUInt160.Zero; - } - - [Benchmark] - public void TestOldUInt160OperatorSmaller() - { - _ = s_oldUInt160 < OldUInt160.Zero; - } - - [Benchmark] - public void TestOldUInt160OperatorSmallerAndEqual() - { - _ = s_oldUInt160 <= OldUInt160.Zero; - } - - [Benchmark] - public void TestGernerator1() - { - _ = new UInt160(); - } - - [Benchmark] - public void TestGernerator2() - { - _ = new UInt160(new byte[20]); - } - - [Benchmark] - public void TestCompareTo() - { - UInt160.Zero.CompareTo(UInt160.Zero); - UInt160.Zero.CompareTo(s_newUInt160); - s_newUInt160.CompareTo(UInt160.Zero); - } - - [Benchmark] - public void TestEquals() - { - UInt160.Zero.Equals(UInt160.Zero); - UInt160.Zero.Equals(s_newUInt160); - s_newUInt160.Equals(null); - } - - [Benchmark] - public void TestParse() - { - _ = UInt160.Parse("0x0000000000000000000000000000000000000000"); - _ = UInt160.Parse("0000000000000000000000000000000000000000"); - } - - [Benchmark] - public void TestTryParse() - { - UInt160.TryParse(null, out _); - UInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); - UInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); - UInt160.TryParse("000000000000000000000000000000000000000", out _); - } - - [Benchmark] - public void TestOperatorLarger() - { - _ = s_newUInt160 > UInt160.Zero; - } - - [Benchmark] - public void TestOperatorLargerAndEqual() - { - _ = s_newUInt160 >= UInt160.Zero; - } - - [Benchmark] - public void TestOperatorSmaller() - { - _ = s_newUInt160 < UInt160.Zero; - } - - [Benchmark] - public void TestOperatorSmallerAndEqual() - { - _ = s_newUInt160 <= UInt160.Zero; + public class Benchmarks_UInt160 + { + static readonly OldUInt160 s_oldUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + static readonly UInt160 s_newUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + + [Benchmark] + public void TestOldUInt160Gernerator1() + { + _ = new OldUInt160(); + } + + [Benchmark] + public void TestOldUInt160Gernerator2() + { + _ = new OldUInt160(new byte[20]); + } + + [Benchmark] + public void TestOldUInt160CompareTo() + { + OldUInt160.Zero.CompareTo(OldUInt160.Zero); + OldUInt160.Zero.CompareTo(s_oldUInt160); + s_oldUInt160.CompareTo(OldUInt160.Zero); + } + + [Benchmark] + public void TestOldUInt160Equals() + { + OldUInt160.Zero.Equals(OldUInt160.Zero); + OldUInt160.Zero.Equals(s_oldUInt160); + s_oldUInt160.Equals(null); + } + + [Benchmark] + public void TestOldUInt160Parse() + { + _ = OldUInt160.Parse("0x0000000000000000000000000000000000000000"); + _ = OldUInt160.Parse("0000000000000000000000000000000000000000"); + } + + [Benchmark] + public void TestOldUInt160TryParse() + { + OldUInt160.TryParse(null, out _); + OldUInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); + OldUInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); + OldUInt160.TryParse("000000000000000000000000000000000000000", out _); + } + + [Benchmark] + public void TestOldUInt160OperatorLarger() + { + _ = s_oldUInt160 > OldUInt160.Zero; + } + + [Benchmark] + public void TestOldUInt160OperatorLargerAndEqual() + { + _ = s_oldUInt160 >= OldUInt160.Zero; + } + + [Benchmark] + public void TestOldUInt160OperatorSmaller() + { + _ = s_oldUInt160 < OldUInt160.Zero; + } + + [Benchmark] + public void TestOldUInt160OperatorSmallerAndEqual() + { + _ = s_oldUInt160 <= OldUInt160.Zero; + } + + [Benchmark] + public void TestGernerator1() + { + _ = new UInt160(); + } + + [Benchmark] + public void TestGernerator2() + { + _ = new UInt160(new byte[20]); + } + + [Benchmark] + public void TestCompareTo() + { + UInt160.Zero.CompareTo(UInt160.Zero); + UInt160.Zero.CompareTo(s_newUInt160); + s_newUInt160.CompareTo(UInt160.Zero); + } + + [Benchmark] + public void TestEquals() + { + UInt160.Zero.Equals(UInt160.Zero); + UInt160.Zero.Equals(s_newUInt160); + s_newUInt160.Equals(null); + } + + [Benchmark] + public void TestParse() + { + _ = UInt160.Parse("0x0000000000000000000000000000000000000000"); + _ = UInt160.Parse("0000000000000000000000000000000000000000"); + } + + [Benchmark] + public void TestTryParse() + { + UInt160.TryParse(null, out _); + UInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); + UInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); + UInt160.TryParse("000000000000000000000000000000000000000", out _); + } + + [Benchmark] + public void TestOperatorLarger() + { + _ = s_newUInt160 > UInt160.Zero; + } + + [Benchmark] + public void TestOperatorLargerAndEqual() + { + _ = s_newUInt160 >= UInt160.Zero; + } + + [Benchmark] + public void TestOperatorSmaller() + { + _ = s_newUInt160 < UInt160.Zero; + } + + [Benchmark] + public void TestOperatorSmallerAndEqual() + { + _ = s_newUInt160 <= UInt160.Zero; + } } } diff --git a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj index 6a4bd93264..4d5a494b00 100644 --- a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj +++ b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj @@ -2,16 +2,16 @@ Exe - net8.0 + net9.0 Neo enable true - + - + diff --git a/benchmarks/Neo.Benchmarks/Program.cs b/benchmarks/Neo.Benchmarks/Program.cs index a64a1ca981..0bee186889 100644 --- a/benchmarks/Neo.Benchmarks/Program.cs +++ b/benchmarks/Neo.Benchmarks/Program.cs @@ -11,7 +11,9 @@ using BenchmarkDotNet.Running; using Neo.Benchmark; +using Neo.SmartContract.Benchmark; // BenchmarkRunner.Run(); BenchmarkRunner.Run(); BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/benchmarks/Neo.Benchmarks/SmartContract/Benchmarks.StorageKey.cs b/benchmarks/Neo.Benchmarks/SmartContract/Benchmarks.StorageKey.cs new file mode 100644 index 0000000000..5129a1dfaf --- /dev/null +++ b/benchmarks/Neo.Benchmarks/SmartContract/Benchmarks.StorageKey.cs @@ -0,0 +1,75 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Benchmarks.StorageKey.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 BenchmarkDotNet.Attributes; +using System.Text; + +namespace Neo.SmartContract.Benchmark +{ + public class Benchmarks_StorageKey + { + // for avoiding overhead of encoding + private static readonly byte[] testBytes = Encoding.ASCII.GetBytes("StorageKey"); + + private const int prefixSize = sizeof(int) + sizeof(byte); + + [Benchmark] + public void KeyBuilder_AddInt() + { + var key = new KeyBuilder(1, 0) + .AddBigEndian(1) + .AddBigEndian(2) + .AddBigEndian(3); + + var bytes = key.ToArray(); + if (bytes.Length != prefixSize + 3 * sizeof(int)) + throw new InvalidOperationException(); + } + + [Benchmark] + public void KeyBuilder_AddIntWithoutPrealloc() + { + var key = new KeyBuilder(1, 0, 0) + .AddBigEndian(1) + .AddBigEndian(2) + .AddBigEndian(3); + + var bytes = key.ToArray(); + if (bytes.Length != prefixSize + 3 * sizeof(int)) + throw new InvalidOperationException(); + } + + [Benchmark] + public void KeyBuilder_AddBytes() + { + var key = new KeyBuilder(1, 0) + .Add(testBytes) + .Add(testBytes) + .Add(testBytes); + + var bytes = key.ToArray(); + if (bytes.Length != prefixSize + 3 * testBytes.Length) + throw new InvalidOperationException(); + } + + [Benchmark] + public void KeyBuilder_AddUInt160() + { + Span value = stackalloc byte[UInt160.Length]; + var key = new KeyBuilder(1, 0) + .Add(new UInt160(value)); + + var bytes = key.ToArray(); + if (bytes.Length != prefixSize + UInt160.Length) + throw new InvalidOperationException(); + } + } +} diff --git a/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj b/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj index d87bccaee5..5f7211e49a 100644 --- a/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj +++ b/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj @@ -2,16 +2,16 @@ Exe - net8.0 + net9.0 Neo.Extensions enable enable - + - + diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs index f5ea579e6f..fb24fe7fa8 100644 --- a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs @@ -11,60 +11,61 @@ using System.Buffers.Binary; -namespace Neo.VM.Benchmark; - -public static class Helper +namespace Neo.VM.Benchmark { - public static void RebuildOffsets(this IReadOnlyList instructions) + public static class Helper { - var offset = 0; - foreach (var instruction in instructions) + public static void RebuildOffsets(this IReadOnlyList instructions) { - instruction._offset = offset; - offset += instruction.Size; + var offset = 0; + foreach (var instruction in instructions) + { + instruction._offset = offset; + offset += instruction.Size; + } } - } - public static void RebuildOperands(this IReadOnlyList instructions) - { - foreach (var instruction in instructions) + public static void RebuildOperands(this IReadOnlyList instructions) { - if (instruction._target is null) continue; - bool isLong; - if (instruction._opCode >= VM.OpCode.JMP && instruction._opCode <= VM.OpCode.CALL_L) - isLong = (instruction._opCode - VM.OpCode.JMP) % 2 != 0; - else - isLong = instruction._opCode == VM.OpCode.PUSHA || instruction._opCode == VM.OpCode.CALLA || instruction._opCode == VM.OpCode.TRY_L || instruction._opCode == VM.OpCode.ENDTRY_L; - if (instruction._opCode == VM.OpCode.TRY || instruction._opCode == VM.OpCode.TRY_L) + foreach (var instruction in instructions) { - var offset1 = (instruction._target._instruction?._offset - instruction._offset) ?? 0; - var offset2 = (instruction._target2!._instruction?._offset - instruction._offset) ?? 0; - if (isLong) - { - instruction._operand = new byte[sizeof(int) + sizeof(int)]; - BinaryPrimitives.WriteInt32LittleEndian(instruction._operand, offset1); - BinaryPrimitives.WriteInt32LittleEndian(instruction._operand.AsSpan(sizeof(int)), offset2); - } + if (instruction._target is null) continue; + bool isLong; + if (instruction._opCode >= VM.OpCode.JMP && instruction._opCode <= VM.OpCode.CALL_L) + isLong = (instruction._opCode - VM.OpCode.JMP) % 2 != 0; else + isLong = instruction._opCode == VM.OpCode.PUSHA || instruction._opCode == VM.OpCode.CALLA || instruction._opCode == VM.OpCode.TRY_L || instruction._opCode == VM.OpCode.ENDTRY_L; + if (instruction._opCode == VM.OpCode.TRY || instruction._opCode == VM.OpCode.TRY_L) { - instruction._operand = new byte[sizeof(sbyte) + sizeof(sbyte)]; - var sbyte1 = checked((sbyte)offset1); - var sbyte2 = checked((sbyte)offset2); - instruction._operand[0] = unchecked((byte)sbyte1); - instruction._operand[1] = unchecked((byte)sbyte2); - } - } - else - { - int offset = instruction._target._instruction!._offset - instruction._offset; - if (isLong) - { - instruction._operand = BitConverter.GetBytes(offset); + var offset1 = (instruction._target._instruction?._offset - instruction._offset) ?? 0; + var offset2 = (instruction._target2!._instruction?._offset - instruction._offset) ?? 0; + if (isLong) + { + instruction._operand = new byte[sizeof(int) + sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(instruction._operand, offset1); + BinaryPrimitives.WriteInt32LittleEndian(instruction._operand.AsSpan(sizeof(int)), offset2); + } + else + { + instruction._operand = new byte[sizeof(sbyte) + sizeof(sbyte)]; + var sbyte1 = checked((sbyte)offset1); + var sbyte2 = checked((sbyte)offset2); + instruction._operand[0] = unchecked((byte)sbyte1); + instruction._operand[1] = unchecked((byte)sbyte2); + } } else { - var sbyte1 = checked((sbyte)offset); - instruction._operand = [unchecked((byte)sbyte1)]; + int offset = instruction._target._instruction!._offset - instruction._offset; + if (isLong) + { + instruction._operand = BitConverter.GetBytes(offset); + } + else + { + var sbyte1 = checked((sbyte)offset); + instruction._operand = [unchecked((byte)sbyte1)]; + } } } } diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs index 5a30aeec10..54a0c3ee98 100644 --- a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs @@ -13,46 +13,47 @@ using System.Diagnostics; using System.Reflection; -namespace Neo.VM.Benchmark; - -[DebuggerDisplay("{_opCode}")] -public class Instruction +namespace Neo.VM.Benchmark { - private static readonly int[] s_operandSizePrefixTable = new int[256]; - private static readonly int[] s_operandSizeTable = new int[256]; + [DebuggerDisplay("{_opCode}")] + public class Instruction + { + private static readonly int[] s_operandSizePrefixTable = new int[256]; + private static readonly int[] s_operandSizeTable = new int[256]; - public VM.OpCode _opCode; - public byte[]? _operand; - public JumpTarget? _target; - public JumpTarget? _target2; - public int _offset; + public VM.OpCode _opCode; + public byte[]? _operand; + public JumpTarget? _target; + public JumpTarget? _target2; + public int _offset; - public int Size - { - get + public int Size { - int prefixSize = s_operandSizePrefixTable[(int)_opCode]; - return prefixSize > 0 - ? sizeof(VM.OpCode) + _operand!.Length - : sizeof(VM.OpCode) + s_operandSizeTable[(int)_opCode]; + get + { + int prefixSize = s_operandSizePrefixTable[(int)_opCode]; + return prefixSize > 0 + ? sizeof(VM.OpCode) + _operand!.Length + : sizeof(VM.OpCode) + s_operandSizeTable[(int)_opCode]; + } } - } - static Instruction() - { - foreach (var field in typeof(VM.OpCode).GetFields(BindingFlags.Public | BindingFlags.Static)) + static Instruction() { - var attribute = field.GetCustomAttribute(); - if (attribute is null) continue; - var index = (int)(VM.OpCode)field.GetValue(null)!; - s_operandSizePrefixTable[index] = attribute.SizePrefix; - s_operandSizeTable[index] = attribute.Size; + foreach (var field in typeof(VM.OpCode).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + var attribute = field.GetCustomAttribute(); + if (attribute is null) continue; + var index = (int)(VM.OpCode)field.GetValue(null)!; + s_operandSizePrefixTable[index] = attribute.SizePrefix; + s_operandSizeTable[index] = attribute.Size; + } } - } - public byte[] ToArray() - { - if (_operand is null) return [(byte)_opCode]; - return _operand.Prepend((byte)_opCode).ToArray(); + public byte[] ToArray() + { + if (_operand is null) return [(byte)_opCode]; + return _operand.Prepend((byte)_opCode).ToArray(); + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs index 21d1b77de2..8fb3469bce 100644 --- a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs @@ -12,231 +12,232 @@ using System.Buffers.Binary; using System.Numerics; -namespace Neo.VM.Benchmark; - -internal class InstructionBuilder +namespace Neo.VM.Benchmark { - internal readonly List _instructions = new(); + internal class InstructionBuilder + { + internal readonly List _instructions = new(); - public InstructionBuilder() { } + public InstructionBuilder() { } - internal Instruction AddInstruction(Instruction instruction) - { - _instructions.Add(instruction); - return instruction; - } + internal Instruction AddInstruction(Instruction instruction) + { + _instructions.Add(instruction); + return instruction; + } - internal Instruction AddInstruction(VM.OpCode opcode) - { - return AddInstruction(new Instruction + internal Instruction AddInstruction(VM.OpCode opcode) { - _opCode = opcode - }); - } + return AddInstruction(new Instruction + { + _opCode = opcode + }); + } - internal Instruction Jump(VM.OpCode opcode, JumpTarget target) - { - return AddInstruction(new Instruction + internal Instruction Jump(VM.OpCode opcode, JumpTarget target) { - _opCode = opcode, - _target = target - }); - } + return AddInstruction(new Instruction + { + _opCode = opcode, + _target = target + }); + } - internal void Push(bool value) - { - AddInstruction(value ? VM.OpCode.PUSHT : VM.OpCode.PUSHF); - } + internal void Push(bool value) + { + AddInstruction(value ? VM.OpCode.PUSHT : VM.OpCode.PUSHF); + } - internal Instruction Ret() => AddInstruction(VM.OpCode.RET); + internal Instruction Ret() => AddInstruction(VM.OpCode.RET); - internal Instruction Push(BigInteger number) - { - if (number >= -1 && number <= 16) return AddInstruction(number == -1 ? VM.OpCode.PUSHM1 : VM.OpCode.PUSH0 + (byte)(int)number); - Span buffer = stackalloc byte[32]; - if (!number.TryWriteBytes(buffer, out var bytesWritten, isUnsigned: false, isBigEndian: false)) - throw new ArgumentOutOfRangeException(nameof(number)); - var instruction = bytesWritten switch + internal Instruction Push(BigInteger number) { - 1 => new Instruction - { - _opCode = VM.OpCode.PUSHINT8, - _operand = PadRight(buffer, bytesWritten, 1, number.Sign < 0).ToArray() - }, - 2 => new Instruction - { - _opCode = VM.OpCode.PUSHINT16, - _operand = PadRight(buffer, bytesWritten, 2, number.Sign < 0).ToArray() - }, - <= 4 => new Instruction + if (number >= -1 && number <= 16) return AddInstruction(number == -1 ? VM.OpCode.PUSHM1 : VM.OpCode.PUSH0 + (byte)(int)number); + Span buffer = stackalloc byte[32]; + if (!number.TryWriteBytes(buffer, out var bytesWritten, isUnsigned: false, isBigEndian: false)) + throw new ArgumentOutOfRangeException(nameof(number)); + var instruction = bytesWritten switch { - _opCode = VM.OpCode.PUSHINT32, - _operand = PadRight(buffer, bytesWritten, 4, number.Sign < 0).ToArray() - }, - <= 8 => new Instruction - { - _opCode = VM.OpCode.PUSHINT64, - _operand = PadRight(buffer, bytesWritten, 8, number.Sign < 0).ToArray() - }, - <= 16 => new Instruction - { - _opCode = VM.OpCode.PUSHINT128, - _operand = PadRight(buffer, bytesWritten, 16, number.Sign < 0).ToArray() - }, - <= 32 => new Instruction - { - _opCode = VM.OpCode.PUSHINT256, - _operand = PadRight(buffer, bytesWritten, 32, number.Sign < 0).ToArray() - }, - _ => throw new ArgumentOutOfRangeException($"Number too large: {bytesWritten}") - }; - AddInstruction(instruction); - return instruction; - } - - internal Instruction Push(string s) - { - return Push(Utility.StrictUTF8.GetBytes(s)); - } + 1 => new Instruction + { + _opCode = VM.OpCode.PUSHINT8, + _operand = PadRight(buffer, bytesWritten, 1, number.Sign < 0).ToArray() + }, + 2 => new Instruction + { + _opCode = VM.OpCode.PUSHINT16, + _operand = PadRight(buffer, bytesWritten, 2, number.Sign < 0).ToArray() + }, + <= 4 => new Instruction + { + _opCode = VM.OpCode.PUSHINT32, + _operand = PadRight(buffer, bytesWritten, 4, number.Sign < 0).ToArray() + }, + <= 8 => new Instruction + { + _opCode = VM.OpCode.PUSHINT64, + _operand = PadRight(buffer, bytesWritten, 8, number.Sign < 0).ToArray() + }, + <= 16 => new Instruction + { + _opCode = VM.OpCode.PUSHINT128, + _operand = PadRight(buffer, bytesWritten, 16, number.Sign < 0).ToArray() + }, + <= 32 => new Instruction + { + _opCode = VM.OpCode.PUSHINT256, + _operand = PadRight(buffer, bytesWritten, 32, number.Sign < 0).ToArray() + }, + _ => throw new ArgumentOutOfRangeException($"Number too large: {bytesWritten}") + }; + AddInstruction(instruction); + return instruction; + } - internal Instruction Push(byte[] data) - { - VM.OpCode opcode; - byte[] buffer; - switch (data.Length) + internal Instruction Push(string s) { - case <= byte.MaxValue: - opcode = VM.OpCode.PUSHDATA1; - buffer = new byte[sizeof(byte) + data.Length]; - buffer[0] = (byte)data.Length; - Buffer.BlockCopy(data, 0, buffer, sizeof(byte), data.Length); - break; - case <= ushort.MaxValue: - opcode = VM.OpCode.PUSHDATA2; - buffer = new byte[sizeof(ushort) + data.Length]; - BinaryPrimitives.WriteUInt16LittleEndian(buffer, (ushort)data.Length); - Buffer.BlockCopy(data, 0, buffer, sizeof(ushort), data.Length); - break; - default: - opcode = VM.OpCode.PUSHDATA4; - buffer = new byte[sizeof(uint) + data.Length]; - BinaryPrimitives.WriteUInt32LittleEndian(buffer, (uint)data.Length); - Buffer.BlockCopy(data, 0, buffer, sizeof(uint), data.Length); - break; + return Push(Utility.StrictUTF8.GetBytes(s)); } - return AddInstruction(new Instruction + + internal Instruction Push(byte[] data) { - _opCode = opcode, - _operand = buffer - }); - } + VM.OpCode opcode; + byte[] buffer; + switch (data.Length) + { + case <= byte.MaxValue: + opcode = VM.OpCode.PUSHDATA1; + buffer = new byte[sizeof(byte) + data.Length]; + buffer[0] = (byte)data.Length; + Buffer.BlockCopy(data, 0, buffer, sizeof(byte), data.Length); + break; + case <= ushort.MaxValue: + opcode = VM.OpCode.PUSHDATA2; + buffer = new byte[sizeof(ushort) + data.Length]; + BinaryPrimitives.WriteUInt16LittleEndian(buffer, (ushort)data.Length); + Buffer.BlockCopy(data, 0, buffer, sizeof(ushort), data.Length); + break; + default: + opcode = VM.OpCode.PUSHDATA4; + buffer = new byte[sizeof(uint) + data.Length]; + BinaryPrimitives.WriteUInt32LittleEndian(buffer, (uint)data.Length); + Buffer.BlockCopy(data, 0, buffer, sizeof(uint), data.Length); + break; + } + return AddInstruction(new Instruction + { + _opCode = opcode, + _operand = buffer + }); + } - internal void Push(object? obj) - { - switch (obj) + internal void Push(object? obj) { - case bool data: - Push(data); - break; - case byte[] data: - Push(data); - break; - case string data: - Push(data); - break; - case BigInteger data: - Push(data); - break; - case char data: - Push((ushort)data); - break; - case sbyte data: - Push(data); - break; - case byte data: - Push(data); - break; - case short data: - Push(data); - break; - case ushort data: - Push(data); - break; - case int data: - Push(data); - break; - case uint data: - Push(data); - break; - case long data: - Push(data); - break; - case ulong data: - Push(data); - break; - case Enum data: - Push(BigInteger.Parse(data.ToString("d"))); - break; - case null: - AddInstruction(VM.OpCode.PUSHNULL); - break; - default: - throw new NotSupportedException($"Unsupported constant value: {obj}"); + switch (obj) + { + case bool data: + Push(data); + break; + case byte[] data: + Push(data); + break; + case string data: + Push(data); + break; + case BigInteger data: + Push(data); + break; + case char data: + Push((ushort)data); + break; + case sbyte data: + Push(data); + break; + case byte data: + Push(data); + break; + case short data: + Push(data); + break; + case ushort data: + Push(data); + break; + case int data: + Push(data); + break; + case uint data: + Push(data); + break; + case long data: + Push(data); + break; + case ulong data: + Push(data); + break; + case Enum data: + Push(BigInteger.Parse(data.ToString("d"))); + break; + case null: + AddInstruction(VM.OpCode.PUSHNULL); + break; + default: + throw new NotSupportedException($"Unsupported constant value: {obj}"); + } } - } - // Helper method to reverse stack items - internal void ReverseStackItems(int count) - { - switch (count) + // Helper method to reverse stack items + internal void ReverseStackItems(int count) { - case 2: - AddInstruction(VM.OpCode.SWAP); - break; - case 3: - AddInstruction(VM.OpCode.REVERSE3); - break; - case 4: - AddInstruction(VM.OpCode.REVERSE4); - break; - default: - Push(count); - AddInstruction(VM.OpCode.REVERSEN); - break; + switch (count) + { + case 2: + AddInstruction(VM.OpCode.SWAP); + break; + case 3: + AddInstruction(VM.OpCode.REVERSE3); + break; + case 4: + AddInstruction(VM.OpCode.REVERSE4); + break; + default: + Push(count); + AddInstruction(VM.OpCode.REVERSEN); + break; + } } - } - internal static ReadOnlySpan PadRight(Span buffer, int dataLength, int padLength, bool negative) - { - byte pad = negative ? (byte)0xff : (byte)0; - for (int x = dataLength; x < padLength; x++) - buffer[x] = pad; - return buffer[..padLength]; - } + internal static ReadOnlySpan PadRight(Span buffer, int dataLength, int padLength, bool negative) + { + byte pad = negative ? (byte)0xff : (byte)0; + for (int x = dataLength; x < padLength; x++) + buffer[x] = pad; + return buffer[..padLength]; + } - internal Instruction IsType(VM.Types.StackItemType type) - { - return AddInstruction(new Instruction + internal Instruction IsType(VM.Types.StackItemType type) { - _opCode = VM.OpCode.ISTYPE, - _operand = [(byte)type] - }); - } + return AddInstruction(new Instruction + { + _opCode = VM.OpCode.ISTYPE, + _operand = [(byte)type] + }); + } - internal Instruction ChangeType(VM.Types.StackItemType type) - { - return AddInstruction(new Instruction + internal Instruction ChangeType(VM.Types.StackItemType type) { - _opCode = VM.OpCode.CONVERT, - _operand = [(byte)type] - }); - } + return AddInstruction(new Instruction + { + _opCode = VM.OpCode.CONVERT, + _operand = [(byte)type] + }); + } - internal byte[] ToArray() - { - var instructions = _instructions.ToArray(); - instructions.RebuildOffsets(); - instructions.RebuildOperands(); - return instructions.Select(p => p.ToArray()).SelectMany(p => p).ToArray(); + internal byte[] ToArray() + { + var instructions = _instructions.ToArray(); + instructions.RebuildOffsets(); + instructions.RebuildOperands(); + return instructions.Select(p => p.ToArray()).SelectMany(p => p).ToArray(); + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs index 246b0e5884..6fe4e24e00 100644 --- a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs @@ -9,9 +9,10 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.VM.Benchmark; - -public class JumpTarget +namespace Neo.VM.Benchmark { - public Instruction? _instruction; + public class JumpTarget + { + public Instruction? _instruction; + } } diff --git a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj index d5a7909a4b..32b3ec9aa0 100644 --- a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj +++ b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj @@ -2,18 +2,18 @@ Exe - net8.0 + net9.0 Neo.VM enable enable - + - + diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs index 515125ddc0..31faa5a770 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs @@ -9,40 +9,41 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.VM.Benchmark.OpCode; - -public class OpCode_ReverseN : OpCodeBase +namespace Neo.VM.Benchmark.OpCode { - protected override byte[] CreateScript(BenchmarkMode benchmarkMode) + public class OpCode_ReverseN : OpCodeBase { - var builder = new InstructionBuilder(); - var initBegin = new JumpTarget(); - builder.AddInstruction(new Instruction { _opCode = VM.OpCode.INITSLOT, _operand = [1, 0] }); - builder.Push(ItemCount); - builder.AddInstruction(VM.OpCode.STLOC0); - initBegin._instruction = builder.AddInstruction(VM.OpCode.NOP); - builder.Push(0); - builder.AddInstruction(VM.OpCode.LDLOC0); - builder.AddInstruction(VM.OpCode.DEC); - builder.AddInstruction(VM.OpCode.STLOC0); - builder.AddInstruction(VM.OpCode.LDLOC0); - builder.Jump(VM.OpCode.JMPIF, initBegin); - if (benchmarkMode == BenchmarkMode.BaseLine) - { - return builder.ToArray(); - } - builder.Push(ItemCount); - builder.AddInstruction(VM.OpCode.REVERSEN); - if (benchmarkMode == BenchmarkMode.OneGAS) + protected override byte[] CreateScript(BenchmarkMode benchmarkMode) { - // just keep running until GAS is exhausted - var loopStart = new JumpTarget { _instruction = builder.AddInstruction(VM.OpCode.NOP) }; + var builder = new InstructionBuilder(); + var initBegin = new JumpTarget(); + builder.AddInstruction(new Instruction { _opCode = VM.OpCode.INITSLOT, _operand = [1, 0] }); + builder.Push(ItemCount); + builder.AddInstruction(VM.OpCode.STLOC0); + initBegin._instruction = builder.AddInstruction(VM.OpCode.NOP); + builder.Push(0); + builder.AddInstruction(VM.OpCode.LDLOC0); + builder.AddInstruction(VM.OpCode.DEC); + builder.AddInstruction(VM.OpCode.STLOC0); + builder.AddInstruction(VM.OpCode.LDLOC0); + builder.Jump(VM.OpCode.JMPIF, initBegin); + if (benchmarkMode == BenchmarkMode.BaseLine) + { + return builder.ToArray(); + } builder.Push(ItemCount); builder.AddInstruction(VM.OpCode.REVERSEN); - builder.Jump(VM.OpCode.JMP, loopStart); - } + if (benchmarkMode == BenchmarkMode.OneGAS) + { + // just keep running until GAS is exhausted + var loopStart = new JumpTarget { _instruction = builder.AddInstruction(VM.OpCode.NOP) }; + builder.Push(ItemCount); + builder.AddInstruction(VM.OpCode.REVERSEN); + builder.Jump(VM.OpCode.JMP, loopStart); + } - return builder.ToArray(); + return builder.ToArray(); + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs index f55abae7ee..7dae2c9074 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs @@ -9,226 +9,227 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.VM.Benchmark.OpCode; - -public class Benchmark_Opcode +namespace Neo.VM.Benchmark.OpCode { - internal static readonly long OneGasDatoshi = 1_0000_0000; - - public static readonly IReadOnlyDictionary OpCodePrices = new Dictionary + public class Benchmark_Opcode { - [VM.OpCode.PUSHINT8] = 1 << 0, - [VM.OpCode.PUSHINT16] = 1 << 0, - [VM.OpCode.PUSHINT32] = 1 << 0, - [VM.OpCode.PUSHINT64] = 1 << 0, - [VM.OpCode.PUSHINT128] = 1 << 2, - [VM.OpCode.PUSHINT256] = 1 << 2, - [VM.OpCode.PUSHT] = 1 << 0, - [VM.OpCode.PUSHF] = 1 << 0, - [VM.OpCode.PUSHA] = 1 << 2, - [VM.OpCode.PUSHNULL] = 1 << 0, - [VM.OpCode.PUSHDATA1] = 1 << 3, - [VM.OpCode.PUSHDATA2] = 1 << 9, - [VM.OpCode.PUSHDATA4] = 1 << 12, - [VM.OpCode.PUSHM1] = 1 << 0, - [VM.OpCode.PUSH0] = 1 << 0, - [VM.OpCode.PUSH1] = 1 << 0, - [VM.OpCode.PUSH2] = 1 << 0, - [VM.OpCode.PUSH3] = 1 << 0, - [VM.OpCode.PUSH4] = 1 << 0, - [VM.OpCode.PUSH5] = 1 << 0, - [VM.OpCode.PUSH6] = 1 << 0, - [VM.OpCode.PUSH7] = 1 << 0, - [VM.OpCode.PUSH8] = 1 << 0, - [VM.OpCode.PUSH9] = 1 << 0, - [VM.OpCode.PUSH10] = 1 << 0, - [VM.OpCode.PUSH11] = 1 << 0, - [VM.OpCode.PUSH12] = 1 << 0, - [VM.OpCode.PUSH13] = 1 << 0, - [VM.OpCode.PUSH14] = 1 << 0, - [VM.OpCode.PUSH15] = 1 << 0, - [VM.OpCode.PUSH16] = 1 << 0, - [VM.OpCode.NOP] = 1 << 0, - [VM.OpCode.JMP] = 1 << 1, - [VM.OpCode.JMP_L] = 1 << 1, - [VM.OpCode.JMPIF] = 1 << 1, - [VM.OpCode.JMPIF_L] = 1 << 1, - [VM.OpCode.JMPIFNOT] = 1 << 1, - [VM.OpCode.JMPIFNOT_L] = 1 << 1, - [VM.OpCode.JMPEQ] = 1 << 1, - [VM.OpCode.JMPEQ_L] = 1 << 1, - [VM.OpCode.JMPNE] = 1 << 1, - [VM.OpCode.JMPNE_L] = 1 << 1, - [VM.OpCode.JMPGT] = 1 << 1, - [VM.OpCode.JMPGT_L] = 1 << 1, - [VM.OpCode.JMPGE] = 1 << 1, - [VM.OpCode.JMPGE_L] = 1 << 1, - [VM.OpCode.JMPLT] = 1 << 1, - [VM.OpCode.JMPLT_L] = 1 << 1, - [VM.OpCode.JMPLE] = 1 << 1, - [VM.OpCode.JMPLE_L] = 1 << 1, - [VM.OpCode.CALL] = 1 << 9, - [VM.OpCode.CALL_L] = 1 << 9, - [VM.OpCode.CALLA] = 1 << 9, - [VM.OpCode.CALLT] = 1 << 15, - [VM.OpCode.ABORT] = 0, - [VM.OpCode.ABORTMSG] = 0, - [VM.OpCode.ASSERT] = 1 << 0, - [VM.OpCode.ASSERTMSG] = 1 << 0, - [VM.OpCode.THROW] = 1 << 9, - [VM.OpCode.TRY] = 1 << 2, - [VM.OpCode.TRY_L] = 1 << 2, - [VM.OpCode.ENDTRY] = 1 << 2, - [VM.OpCode.ENDTRY_L] = 1 << 2, - [VM.OpCode.ENDFINALLY] = 1 << 2, - [VM.OpCode.RET] = 0, - [VM.OpCode.SYSCALL] = 0, - [VM.OpCode.DEPTH] = 1 << 1, - [VM.OpCode.DROP] = 1 << 1, - [VM.OpCode.NIP] = 1 << 1, - [VM.OpCode.XDROP] = 1 << 4, - [VM.OpCode.CLEAR] = 1 << 4, - [VM.OpCode.DUP] = 1 << 1, - [VM.OpCode.OVER] = 1 << 1, - [VM.OpCode.PICK] = 1 << 1, - [VM.OpCode.TUCK] = 1 << 1, - [VM.OpCode.SWAP] = 1 << 1, - [VM.OpCode.ROT] = 1 << 1, - [VM.OpCode.ROLL] = 1 << 4, - [VM.OpCode.REVERSE3] = 1 << 1, - [VM.OpCode.REVERSE4] = 1 << 1, - [VM.OpCode.REVERSEN] = 1 << 4, - [VM.OpCode.INITSSLOT] = 1 << 4, - [VM.OpCode.INITSLOT] = 1 << 6, - [VM.OpCode.LDSFLD0] = 1 << 1, - [VM.OpCode.LDSFLD1] = 1 << 1, - [VM.OpCode.LDSFLD2] = 1 << 1, - [VM.OpCode.LDSFLD3] = 1 << 1, - [VM.OpCode.LDSFLD4] = 1 << 1, - [VM.OpCode.LDSFLD5] = 1 << 1, - [VM.OpCode.LDSFLD6] = 1 << 1, - [VM.OpCode.LDSFLD] = 1 << 1, - [VM.OpCode.STSFLD0] = 1 << 1, - [VM.OpCode.STSFLD1] = 1 << 1, - [VM.OpCode.STSFLD2] = 1 << 1, - [VM.OpCode.STSFLD3] = 1 << 1, - [VM.OpCode.STSFLD4] = 1 << 1, - [VM.OpCode.STSFLD5] = 1 << 1, - [VM.OpCode.STSFLD6] = 1 << 1, - [VM.OpCode.STSFLD] = 1 << 1, - [VM.OpCode.LDLOC0] = 1 << 1, - [VM.OpCode.LDLOC1] = 1 << 1, - [VM.OpCode.LDLOC2] = 1 << 1, - [VM.OpCode.LDLOC3] = 1 << 1, - [VM.OpCode.LDLOC4] = 1 << 1, - [VM.OpCode.LDLOC5] = 1 << 1, - [VM.OpCode.LDLOC6] = 1 << 1, - [VM.OpCode.LDLOC] = 1 << 1, - [VM.OpCode.STLOC0] = 1 << 1, - [VM.OpCode.STLOC1] = 1 << 1, - [VM.OpCode.STLOC2] = 1 << 1, - [VM.OpCode.STLOC3] = 1 << 1, - [VM.OpCode.STLOC4] = 1 << 1, - [VM.OpCode.STLOC5] = 1 << 1, - [VM.OpCode.STLOC6] = 1 << 1, - [VM.OpCode.STLOC] = 1 << 1, - [VM.OpCode.LDARG0] = 1 << 1, - [VM.OpCode.LDARG1] = 1 << 1, - [VM.OpCode.LDARG2] = 1 << 1, - [VM.OpCode.LDARG3] = 1 << 1, - [VM.OpCode.LDARG4] = 1 << 1, - [VM.OpCode.LDARG5] = 1 << 1, - [VM.OpCode.LDARG6] = 1 << 1, - [VM.OpCode.LDARG] = 1 << 1, - [VM.OpCode.STARG0] = 1 << 1, - [VM.OpCode.STARG1] = 1 << 1, - [VM.OpCode.STARG2] = 1 << 1, - [VM.OpCode.STARG3] = 1 << 1, - [VM.OpCode.STARG4] = 1 << 1, - [VM.OpCode.STARG5] = 1 << 1, - [VM.OpCode.STARG6] = 1 << 1, - [VM.OpCode.STARG] = 1 << 1, - [VM.OpCode.NEWBUFFER] = 1 << 8, - [VM.OpCode.MEMCPY] = 1 << 11, - [VM.OpCode.CAT] = 1 << 11, - [VM.OpCode.SUBSTR] = 1 << 11, - [VM.OpCode.LEFT] = 1 << 11, - [VM.OpCode.RIGHT] = 1 << 11, - [VM.OpCode.INVERT] = 1 << 2, - [VM.OpCode.AND] = 1 << 3, - [VM.OpCode.OR] = 1 << 3, - [VM.OpCode.XOR] = 1 << 3, - [VM.OpCode.EQUAL] = 1 << 5, - [VM.OpCode.NOTEQUAL] = 1 << 5, - [VM.OpCode.SIGN] = 1 << 2, - [VM.OpCode.ABS] = 1 << 2, - [VM.OpCode.NEGATE] = 1 << 2, - [VM.OpCode.INC] = 1 << 2, - [VM.OpCode.DEC] = 1 << 2, - [VM.OpCode.ADD] = 1 << 3, - [VM.OpCode.SUB] = 1 << 3, - [VM.OpCode.MUL] = 1 << 3, - [VM.OpCode.DIV] = 1 << 3, - [VM.OpCode.MOD] = 1 << 3, - [VM.OpCode.POW] = 1 << 6, - [VM.OpCode.SQRT] = 1 << 6, - [VM.OpCode.MODMUL] = 1 << 5, - [VM.OpCode.MODPOW] = 1 << 11, - [VM.OpCode.SHL] = 1 << 3, - [VM.OpCode.SHR] = 1 << 3, - [VM.OpCode.NOT] = 1 << 2, - [VM.OpCode.BOOLAND] = 1 << 3, - [VM.OpCode.BOOLOR] = 1 << 3, - [VM.OpCode.NZ] = 1 << 2, - [VM.OpCode.NUMEQUAL] = 1 << 3, - [VM.OpCode.NUMNOTEQUAL] = 1 << 3, - [VM.OpCode.LT] = 1 << 3, - [VM.OpCode.LE] = 1 << 3, - [VM.OpCode.GT] = 1 << 3, - [VM.OpCode.GE] = 1 << 3, - [VM.OpCode.MIN] = 1 << 3, - [VM.OpCode.MAX] = 1 << 3, - [VM.OpCode.WITHIN] = 1 << 3, - [VM.OpCode.PACKMAP] = 1 << 11, - [VM.OpCode.PACKSTRUCT] = 1 << 11, - [VM.OpCode.PACK] = 1 << 11, - [VM.OpCode.UNPACK] = 1 << 11, - [VM.OpCode.NEWARRAY0] = 1 << 4, - [VM.OpCode.NEWARRAY] = 1 << 9, - [VM.OpCode.NEWARRAY_T] = 1 << 9, - [VM.OpCode.NEWSTRUCT0] = 1 << 4, - [VM.OpCode.NEWSTRUCT] = 1 << 9, - [VM.OpCode.NEWMAP] = 1 << 3, - [VM.OpCode.SIZE] = 1 << 2, - [VM.OpCode.HASKEY] = 1 << 6, - [VM.OpCode.KEYS] = 1 << 4, - [VM.OpCode.VALUES] = 1 << 13, - [VM.OpCode.PICKITEM] = 1 << 6, - [VM.OpCode.APPEND] = 1 << 13, - [VM.OpCode.SETITEM] = 1 << 13, - [VM.OpCode.REVERSEITEMS] = 1 << 13, - [VM.OpCode.REMOVE] = 1 << 4, - [VM.OpCode.CLEARITEMS] = 1 << 4, - [VM.OpCode.POPITEM] = 1 << 4, - [VM.OpCode.ISNULL] = 1 << 1, - [VM.OpCode.ISTYPE] = 1 << 1, - [VM.OpCode.CONVERT] = 1 << 13, - }; + internal static readonly long OneGasDatoshi = 1_0000_0000; - internal static void RunScript(byte[] script) - { - LoadScript(script).ExecuteBenchmark(); - } + public static readonly IReadOnlyDictionary OpCodePrices = new Dictionary + { + [VM.OpCode.PUSHINT8] = 1 << 0, + [VM.OpCode.PUSHINT16] = 1 << 0, + [VM.OpCode.PUSHINT32] = 1 << 0, + [VM.OpCode.PUSHINT64] = 1 << 0, + [VM.OpCode.PUSHINT128] = 1 << 2, + [VM.OpCode.PUSHINT256] = 1 << 2, + [VM.OpCode.PUSHT] = 1 << 0, + [VM.OpCode.PUSHF] = 1 << 0, + [VM.OpCode.PUSHA] = 1 << 2, + [VM.OpCode.PUSHNULL] = 1 << 0, + [VM.OpCode.PUSHDATA1] = 1 << 3, + [VM.OpCode.PUSHDATA2] = 1 << 9, + [VM.OpCode.PUSHDATA4] = 1 << 12, + [VM.OpCode.PUSHM1] = 1 << 0, + [VM.OpCode.PUSH0] = 1 << 0, + [VM.OpCode.PUSH1] = 1 << 0, + [VM.OpCode.PUSH2] = 1 << 0, + [VM.OpCode.PUSH3] = 1 << 0, + [VM.OpCode.PUSH4] = 1 << 0, + [VM.OpCode.PUSH5] = 1 << 0, + [VM.OpCode.PUSH6] = 1 << 0, + [VM.OpCode.PUSH7] = 1 << 0, + [VM.OpCode.PUSH8] = 1 << 0, + [VM.OpCode.PUSH9] = 1 << 0, + [VM.OpCode.PUSH10] = 1 << 0, + [VM.OpCode.PUSH11] = 1 << 0, + [VM.OpCode.PUSH12] = 1 << 0, + [VM.OpCode.PUSH13] = 1 << 0, + [VM.OpCode.PUSH14] = 1 << 0, + [VM.OpCode.PUSH15] = 1 << 0, + [VM.OpCode.PUSH16] = 1 << 0, + [VM.OpCode.NOP] = 1 << 0, + [VM.OpCode.JMP] = 1 << 1, + [VM.OpCode.JMP_L] = 1 << 1, + [VM.OpCode.JMPIF] = 1 << 1, + [VM.OpCode.JMPIF_L] = 1 << 1, + [VM.OpCode.JMPIFNOT] = 1 << 1, + [VM.OpCode.JMPIFNOT_L] = 1 << 1, + [VM.OpCode.JMPEQ] = 1 << 1, + [VM.OpCode.JMPEQ_L] = 1 << 1, + [VM.OpCode.JMPNE] = 1 << 1, + [VM.OpCode.JMPNE_L] = 1 << 1, + [VM.OpCode.JMPGT] = 1 << 1, + [VM.OpCode.JMPGT_L] = 1 << 1, + [VM.OpCode.JMPGE] = 1 << 1, + [VM.OpCode.JMPGE_L] = 1 << 1, + [VM.OpCode.JMPLT] = 1 << 1, + [VM.OpCode.JMPLT_L] = 1 << 1, + [VM.OpCode.JMPLE] = 1 << 1, + [VM.OpCode.JMPLE_L] = 1 << 1, + [VM.OpCode.CALL] = 1 << 9, + [VM.OpCode.CALL_L] = 1 << 9, + [VM.OpCode.CALLA] = 1 << 9, + [VM.OpCode.CALLT] = 1 << 15, + [VM.OpCode.ABORT] = 0, + [VM.OpCode.ABORTMSG] = 0, + [VM.OpCode.ASSERT] = 1 << 0, + [VM.OpCode.ASSERTMSG] = 1 << 0, + [VM.OpCode.THROW] = 1 << 9, + [VM.OpCode.TRY] = 1 << 2, + [VM.OpCode.TRY_L] = 1 << 2, + [VM.OpCode.ENDTRY] = 1 << 2, + [VM.OpCode.ENDTRY_L] = 1 << 2, + [VM.OpCode.ENDFINALLY] = 1 << 2, + [VM.OpCode.RET] = 0, + [VM.OpCode.SYSCALL] = 0, + [VM.OpCode.DEPTH] = 1 << 1, + [VM.OpCode.DROP] = 1 << 1, + [VM.OpCode.NIP] = 1 << 1, + [VM.OpCode.XDROP] = 1 << 4, + [VM.OpCode.CLEAR] = 1 << 4, + [VM.OpCode.DUP] = 1 << 1, + [VM.OpCode.OVER] = 1 << 1, + [VM.OpCode.PICK] = 1 << 1, + [VM.OpCode.TUCK] = 1 << 1, + [VM.OpCode.SWAP] = 1 << 1, + [VM.OpCode.ROT] = 1 << 1, + [VM.OpCode.ROLL] = 1 << 4, + [VM.OpCode.REVERSE3] = 1 << 1, + [VM.OpCode.REVERSE4] = 1 << 1, + [VM.OpCode.REVERSEN] = 1 << 4, + [VM.OpCode.INITSSLOT] = 1 << 4, + [VM.OpCode.INITSLOT] = 1 << 6, + [VM.OpCode.LDSFLD0] = 1 << 1, + [VM.OpCode.LDSFLD1] = 1 << 1, + [VM.OpCode.LDSFLD2] = 1 << 1, + [VM.OpCode.LDSFLD3] = 1 << 1, + [VM.OpCode.LDSFLD4] = 1 << 1, + [VM.OpCode.LDSFLD5] = 1 << 1, + [VM.OpCode.LDSFLD6] = 1 << 1, + [VM.OpCode.LDSFLD] = 1 << 1, + [VM.OpCode.STSFLD0] = 1 << 1, + [VM.OpCode.STSFLD1] = 1 << 1, + [VM.OpCode.STSFLD2] = 1 << 1, + [VM.OpCode.STSFLD3] = 1 << 1, + [VM.OpCode.STSFLD4] = 1 << 1, + [VM.OpCode.STSFLD5] = 1 << 1, + [VM.OpCode.STSFLD6] = 1 << 1, + [VM.OpCode.STSFLD] = 1 << 1, + [VM.OpCode.LDLOC0] = 1 << 1, + [VM.OpCode.LDLOC1] = 1 << 1, + [VM.OpCode.LDLOC2] = 1 << 1, + [VM.OpCode.LDLOC3] = 1 << 1, + [VM.OpCode.LDLOC4] = 1 << 1, + [VM.OpCode.LDLOC5] = 1 << 1, + [VM.OpCode.LDLOC6] = 1 << 1, + [VM.OpCode.LDLOC] = 1 << 1, + [VM.OpCode.STLOC0] = 1 << 1, + [VM.OpCode.STLOC1] = 1 << 1, + [VM.OpCode.STLOC2] = 1 << 1, + [VM.OpCode.STLOC3] = 1 << 1, + [VM.OpCode.STLOC4] = 1 << 1, + [VM.OpCode.STLOC5] = 1 << 1, + [VM.OpCode.STLOC6] = 1 << 1, + [VM.OpCode.STLOC] = 1 << 1, + [VM.OpCode.LDARG0] = 1 << 1, + [VM.OpCode.LDARG1] = 1 << 1, + [VM.OpCode.LDARG2] = 1 << 1, + [VM.OpCode.LDARG3] = 1 << 1, + [VM.OpCode.LDARG4] = 1 << 1, + [VM.OpCode.LDARG5] = 1 << 1, + [VM.OpCode.LDARG6] = 1 << 1, + [VM.OpCode.LDARG] = 1 << 1, + [VM.OpCode.STARG0] = 1 << 1, + [VM.OpCode.STARG1] = 1 << 1, + [VM.OpCode.STARG2] = 1 << 1, + [VM.OpCode.STARG3] = 1 << 1, + [VM.OpCode.STARG4] = 1 << 1, + [VM.OpCode.STARG5] = 1 << 1, + [VM.OpCode.STARG6] = 1 << 1, + [VM.OpCode.STARG] = 1 << 1, + [VM.OpCode.NEWBUFFER] = 1 << 8, + [VM.OpCode.MEMCPY] = 1 << 11, + [VM.OpCode.CAT] = 1 << 11, + [VM.OpCode.SUBSTR] = 1 << 11, + [VM.OpCode.LEFT] = 1 << 11, + [VM.OpCode.RIGHT] = 1 << 11, + [VM.OpCode.INVERT] = 1 << 2, + [VM.OpCode.AND] = 1 << 3, + [VM.OpCode.OR] = 1 << 3, + [VM.OpCode.XOR] = 1 << 3, + [VM.OpCode.EQUAL] = 1 << 5, + [VM.OpCode.NOTEQUAL] = 1 << 5, + [VM.OpCode.SIGN] = 1 << 2, + [VM.OpCode.ABS] = 1 << 2, + [VM.OpCode.NEGATE] = 1 << 2, + [VM.OpCode.INC] = 1 << 2, + [VM.OpCode.DEC] = 1 << 2, + [VM.OpCode.ADD] = 1 << 3, + [VM.OpCode.SUB] = 1 << 3, + [VM.OpCode.MUL] = 1 << 3, + [VM.OpCode.DIV] = 1 << 3, + [VM.OpCode.MOD] = 1 << 3, + [VM.OpCode.POW] = 1 << 6, + [VM.OpCode.SQRT] = 1 << 6, + [VM.OpCode.MODMUL] = 1 << 5, + [VM.OpCode.MODPOW] = 1 << 11, + [VM.OpCode.SHL] = 1 << 3, + [VM.OpCode.SHR] = 1 << 3, + [VM.OpCode.NOT] = 1 << 2, + [VM.OpCode.BOOLAND] = 1 << 3, + [VM.OpCode.BOOLOR] = 1 << 3, + [VM.OpCode.NZ] = 1 << 2, + [VM.OpCode.NUMEQUAL] = 1 << 3, + [VM.OpCode.NUMNOTEQUAL] = 1 << 3, + [VM.OpCode.LT] = 1 << 3, + [VM.OpCode.LE] = 1 << 3, + [VM.OpCode.GT] = 1 << 3, + [VM.OpCode.GE] = 1 << 3, + [VM.OpCode.MIN] = 1 << 3, + [VM.OpCode.MAX] = 1 << 3, + [VM.OpCode.WITHIN] = 1 << 3, + [VM.OpCode.PACKMAP] = 1 << 11, + [VM.OpCode.PACKSTRUCT] = 1 << 11, + [VM.OpCode.PACK] = 1 << 11, + [VM.OpCode.UNPACK] = 1 << 11, + [VM.OpCode.NEWARRAY0] = 1 << 4, + [VM.OpCode.NEWARRAY] = 1 << 9, + [VM.OpCode.NEWARRAY_T] = 1 << 9, + [VM.OpCode.NEWSTRUCT0] = 1 << 4, + [VM.OpCode.NEWSTRUCT] = 1 << 9, + [VM.OpCode.NEWMAP] = 1 << 3, + [VM.OpCode.SIZE] = 1 << 2, + [VM.OpCode.HASKEY] = 1 << 6, + [VM.OpCode.KEYS] = 1 << 4, + [VM.OpCode.VALUES] = 1 << 13, + [VM.OpCode.PICKITEM] = 1 << 6, + [VM.OpCode.APPEND] = 1 << 13, + [VM.OpCode.SETITEM] = 1 << 13, + [VM.OpCode.REVERSEITEMS] = 1 << 13, + [VM.OpCode.REMOVE] = 1 << 4, + [VM.OpCode.CLEARITEMS] = 1 << 4, + [VM.OpCode.POPITEM] = 1 << 4, + [VM.OpCode.ISNULL] = 1 << 1, + [VM.OpCode.ISTYPE] = 1 << 1, + [VM.OpCode.CONVERT] = 1 << 13, + }; - internal static BenchmarkEngine RunScriptUntil(byte[] script, VM.OpCode opCode) - { - return LoadScript(script).ExecuteUntil(opCode); - } + internal static void RunScript(byte[] script) + { + LoadScript(script).ExecuteBenchmark(); + } - internal static BenchmarkEngine LoadScript(byte[] script) - { - var engine = new BenchmarkEngine(); - engine.LoadScript(script); - return engine; + internal static BenchmarkEngine RunScriptUntil(byte[] script, VM.OpCode opCode) + { + return LoadScript(script).ExecuteUntil(opCode); + } + + internal static BenchmarkEngine LoadScript(byte[] script) + { + var engine = new BenchmarkEngine(); + engine.LoadScript(script); + return engine; + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs index 50f80c57e9..f1452f2e6c 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs @@ -13,178 +13,179 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -namespace Neo.VM.Benchmark.OpCode; - -///

-/// A simple benchmark engine for . -/// -public class BenchmarkEngine : TestEngine +namespace Neo.VM.Benchmark.OpCode { - private readonly Dictionary _opcodeStats = new(); - private readonly Dictionary> _breakPoints = new(); - private long _gasConsumed = 0; - /// - /// Add a breakpoint at the specified position of the specified script. The VM will break the execution when it reaches the breakpoint. + /// A simple benchmark engine for . /// - /// The script to add the breakpoint. - /// The position of the breakpoint in the script. - public void AddBreakPoint(Script script, uint position) + public class BenchmarkEngine : TestEngine { - if (!_breakPoints.TryGetValue(script, out var hashset)) - { - hashset = []; - _breakPoints.Add(script, hashset); - } - hashset.Add(position); - } + private readonly Dictionary _opcodeStats = new(); + private readonly Dictionary> _breakPoints = new(); + private long _gasConsumed = 0; - /// - /// Start or continue execution of the VM. - /// - /// Returns the state of the VM after the execution. - public BenchmarkEngine ExecuteUntil(VM.OpCode opCode) - { - if (State == VMState.BREAK) - State = VMState.NONE; - while (State == VMState.NONE) + /// + /// Add a breakpoint at the specified position of the specified script. The VM will break the execution when it reaches the breakpoint. + /// + /// The script to add the breakpoint. + /// The position of the breakpoint in the script. + public void AddBreakPoint(Script script, uint position) { - ExecuteNext(); - try + if (!_breakPoints.TryGetValue(script, out var hashset)) { - var instruction = CurrentContext!.CurrentInstruction!.OpCode; - if (instruction == opCode) break; + hashset = []; + _breakPoints.Add(script, hashset); } - catch + hashset.Add(position); + } + + /// + /// Start or continue execution of the VM. + /// + /// Returns the state of the VM after the execution. + public BenchmarkEngine ExecuteUntil(VM.OpCode opCode) + { + if (State == VMState.BREAK) + State = VMState.NONE; + while (State == VMState.NONE) { - break; + ExecuteNext(); + try + { + var instruction = CurrentContext!.CurrentInstruction!.OpCode; + if (instruction == opCode) break; + } + catch + { + break; + } } + return this; } - return this; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ExecuteBenchmark() - { - while (State != VMState.HALT && State != VMState.FAULT) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteBenchmark() { + while (State != VMState.HALT && State != VMState.FAULT) + { #if DEBUG - var stopwatch = Stopwatch.StartNew(); + var stopwatch = Stopwatch.StartNew(); #endif - ExecuteNext(); + ExecuteNext(); #if DEBUG - stopwatch.Stop(); - UpdateOpcodeStats(CurrentContext!.CurrentInstruction!.OpCode, stopwatch.Elapsed); + stopwatch.Stop(); + UpdateOpcodeStats(CurrentContext!.CurrentInstruction!.OpCode, stopwatch.Elapsed); #endif - } + } #if DEBUG - PrintOpcodeStats(); + PrintOpcodeStats(); #endif - } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ExecuteOneGASBenchmark() - { - while (State != VMState.HALT && State != VMState.FAULT) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteOneGASBenchmark() { - var instruction = CurrentContext!.CurrentInstruction ?? VM.Instruction.RET; - _gasConsumed += Benchmark_Opcode.OpCodePrices[instruction.OpCode]; - if (_gasConsumed >= Benchmark_Opcode.OneGasDatoshi) + while (State != VMState.HALT && State != VMState.FAULT) { - State = VMState.HALT; - } + var instruction = CurrentContext!.CurrentInstruction ?? VM.Instruction.RET; + _gasConsumed += Benchmark_Opcode.OpCodePrices[instruction.OpCode]; + if (_gasConsumed >= Benchmark_Opcode.OneGasDatoshi) + { + State = VMState.HALT; + } #if DEBUG - var stopwatch = Stopwatch.StartNew(); + var stopwatch = Stopwatch.StartNew(); #endif - ExecuteNext(); + ExecuteNext(); #if DEBUG - stopwatch.Stop(); - UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); + stopwatch.Stop(); + UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); #endif - } + } #if DEBUG - PrintOpcodeStats(); + PrintOpcodeStats(); #endif - } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ExecuteTwentyGASBenchmark() - { - while (State != VMState.HALT && State != VMState.FAULT) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteTwentyGASBenchmark() { - var instruction = CurrentContext!.CurrentInstruction ?? VM.Instruction.RET; - _gasConsumed += Benchmark_Opcode.OpCodePrices[instruction.OpCode]; - if (_gasConsumed >= 20 * Benchmark_Opcode.OneGasDatoshi) + while (State != VMState.HALT && State != VMState.FAULT) { - State = VMState.HALT; - } + var instruction = CurrentContext!.CurrentInstruction ?? VM.Instruction.RET; + _gasConsumed += Benchmark_Opcode.OpCodePrices[instruction.OpCode]; + if (_gasConsumed >= 20 * Benchmark_Opcode.OneGasDatoshi) + { + State = VMState.HALT; + } #if DEBUG - var stopwatch = Stopwatch.StartNew(); + var stopwatch = Stopwatch.StartNew(); #endif - ExecuteNext(); + ExecuteNext(); #if DEBUG - stopwatch.Stop(); - UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); + stopwatch.Stop(); + UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); #endif - } + } #if DEBUG - PrintOpcodeStats(); + PrintOpcodeStats(); #endif - } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ExecuteOpCodesBenchmark() - { - while (State != VMState.HALT && State != VMState.FAULT) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteOpCodesBenchmark() { - var instruction = CurrentContext!.CurrentInstruction ?? VM.Instruction.RET; - _gasConsumed += Benchmark_Opcode.OpCodePrices[instruction.OpCode]; - if (_gasConsumed >= Benchmark_Opcode.OneGasDatoshi) + while (State != VMState.HALT && State != VMState.FAULT) { - State = VMState.HALT; - } + var instruction = CurrentContext!.CurrentInstruction ?? VM.Instruction.RET; + _gasConsumed += Benchmark_Opcode.OpCodePrices[instruction.OpCode]; + if (_gasConsumed >= Benchmark_Opcode.OneGasDatoshi) + { + State = VMState.HALT; + } #if DEBUG - var stopwatch = Stopwatch.StartNew(); + var stopwatch = Stopwatch.StartNew(); #endif - ExecuteNext(); + ExecuteNext(); #if DEBUG - stopwatch.Stop(); - UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); + stopwatch.Stop(); + UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); #endif - } + } #if DEBUG - PrintOpcodeStats(); + PrintOpcodeStats(); #endif - } - - protected override void OnFault(Exception ex) - { - base.OnFault(ex); - // throw ex; - } + } - private void UpdateOpcodeStats(VM.OpCode opcode, TimeSpan elapsed) - { - if (!_opcodeStats.TryGetValue(opcode, out var value)) + protected override void OnFault(Exception ex) { - _opcodeStats[opcode] = (1, elapsed); + base.OnFault(ex); + // throw ex; } - else + + private void UpdateOpcodeStats(VM.OpCode opcode, TimeSpan elapsed) { - var (count, totalTime) = value; - _opcodeStats[opcode] = (count + 1, totalTime + elapsed); + if (!_opcodeStats.TryGetValue(opcode, out var value)) + { + _opcodeStats[opcode] = (1, elapsed); + } + else + { + var (count, totalTime) = value; + _opcodeStats[opcode] = (count + 1, totalTime + elapsed); + } } - } - private void PrintOpcodeStats() - { - Console.WriteLine("Opcode Statistics:"); - foreach (var kvp in _opcodeStats) + private void PrintOpcodeStats() { - Console.WriteLine($"{kvp.Key,-15} " + - $"Count: {kvp.Value.Count,8} " + - $"Total Time: {kvp.Value.TotalTime.TotalMilliseconds * 1000,10:F2} μs " + - $"Avg Time: {kvp.Value.TotalTime.TotalMilliseconds * 1000 / kvp.Value.Count,10:F2} μs"); + Console.WriteLine("Opcode Statistics:"); + foreach (var kvp in _opcodeStats) + { + Console.WriteLine($"{kvp.Key,-15} " + + $"Count: {kvp.Value.Count,8} " + + $"Total Time: {kvp.Value.TotalTime.TotalMilliseconds * 1000,10:F2} μs " + + $"Avg Time: {kvp.Value.TotalTime.TotalMilliseconds * 1000 / kvp.Value.Count,10:F2} μs"); + } } } } diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs index 792ff04f16..88fc793c3d 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs @@ -9,11 +9,12 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.VM.Benchmark.OpCode; - -public enum BenchmarkMode +namespace Neo.VM.Benchmark.OpCode { - SimpleOpCode, - OneGAS, - BaseLine + public enum BenchmarkMode + { + SimpleOpCode, + OneGAS, + BaseLine + } } diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs index 46d5be624f..914e947ea1 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs @@ -11,35 +11,36 @@ using BenchmarkDotNet.Attributes; -namespace Neo.VM.Benchmark.OpCode; - -public abstract class OpCodeBase +namespace Neo.VM.Benchmark.OpCode { - [Params(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2040)] - public int ItemCount { get; set; } = 10; - protected byte[] baseLineScript = Array.Empty(); - protected byte[] script = Array.Empty(); - protected byte[] multiScript = Array.Empty(); - - [GlobalSetup] - public void Setup() + public abstract class OpCodeBase { - script = CreateScript(BenchmarkMode.SimpleOpCode); - multiScript = CreateScript(BenchmarkMode.OneGAS); - baseLineScript = CreateScript(BenchmarkMode.BaseLine); + [Params(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2040)] + public int ItemCount { get; set; } = 10; + protected byte[] baseLineScript = Array.Empty(); + protected byte[] script = Array.Empty(); + protected byte[] multiScript = Array.Empty(); + + [GlobalSetup] + public void Setup() + { + script = CreateScript(BenchmarkMode.SimpleOpCode); + multiScript = CreateScript(BenchmarkMode.OneGAS); + baseLineScript = CreateScript(BenchmarkMode.BaseLine); + } + + [Benchmark(Baseline = true)] + public void Bench_BaseLine() => Benchmark_Opcode.RunScript(baseLineScript); + + [Benchmark] + public void Bench_OneOpCode() => Benchmark_Opcode.RunScript(script); + + /// + /// Benchmark how long 1 GAS can run. + /// + [Benchmark] + public void Bench_OneGAS() => Benchmark_Opcode.LoadScript(multiScript).ExecuteOneGASBenchmark(); + + protected abstract byte[] CreateScript(BenchmarkMode benchmarkMode); } - - [Benchmark(Baseline = true)] - public void Bench_BaseLine() => Benchmark_Opcode.RunScript(baseLineScript); - - [Benchmark] - public void Bench_OneOpCode() => Benchmark_Opcode.RunScript(script); - - /// - /// Benchmark how long 1 GAS can run. - /// - [Benchmark] - public void Bench_OneGAS() => Benchmark_Opcode.LoadScript(multiScript).ExecuteOneGASBenchmark(); - - protected abstract byte[] CreateScript(BenchmarkMode benchmarkMode); } diff --git a/benchmarks/Neo.VM.Benchmarks/TestArray.cs b/benchmarks/Neo.VM.Benchmarks/TestArray.cs index 0799040e52..8a0d84a9e1 100644 --- a/benchmarks/Neo.VM.Benchmarks/TestArray.cs +++ b/benchmarks/Neo.VM.Benchmarks/TestArray.cs @@ -12,130 +12,131 @@ using Neo.VM.Types; using System.Collections; -namespace Neo.VM.Benchmark; - -public class TestArray : CompoundType, IReadOnlyList +namespace Neo.VM.Benchmark { - protected readonly List _array; - - /// - /// Get or set item in the array. - /// - /// The index of the item in the array. - /// The item at the specified index. - public StackItem this[int index] + public class TestArray : CompoundType, IReadOnlyList { - get => _array[index]; - set + protected readonly List _array; + + /// + /// Get or set item in the array. + /// + /// The index of the item in the array. + /// The item at the specified index. + public StackItem this[int index] { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); - ReferenceCounter?.RemoveReference(_array[index], this); - _array[index] = value; - ReferenceCounter?.AddReference(value, this); + get => _array[index]; + set + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + ReferenceCounter?.RemoveReference(_array[index], this); + _array[index] = value; + ReferenceCounter?.AddReference(value, this); + } } - } - /// - /// The number of items in the array. - /// - public override int Count => _array.Count; - public override IEnumerable SubItems => _array; - public override int SubItemsCount => _array.Count; - public override StackItemType Type => StackItemType.Array; + /// + /// The number of items in the array. + /// + public override int Count => _array.Count; + public override IEnumerable SubItems => _array; + public override int SubItemsCount => _array.Count; + public override StackItemType Type => StackItemType.Array; - /// - /// Create an array containing the specified items. - /// - /// The items to be included in the array. - public TestArray(IEnumerable? items = null) - : this(null, items) - { - } + /// + /// Create an array containing the specified items. + /// + /// The items to be included in the array. + public TestArray(IEnumerable? items = null) + : this(null, items) + { + } - /// - /// Create an array containing the specified items. And make the array use the specified . - /// - /// The to be used by this array. - /// The items to be included in the array. - public TestArray(IReferenceCounter? referenceCounter, IEnumerable? items = null) - : base(referenceCounter) - { - _array = items switch + /// + /// Create an array containing the specified items. And make the array use the specified . + /// + /// The to be used by this array. + /// The items to be included in the array. + public TestArray(IReferenceCounter? referenceCounter, IEnumerable? items = null) + : base(referenceCounter) { - null => new List(), - List list => list, - _ => new List(items) - }; - if (referenceCounter != null) - foreach (StackItem item in _array) - referenceCounter.AddReference(item, this); - } + _array = items switch + { + null => new List(), + List list => list, + _ => new List(items) + }; + if (referenceCounter != null) + foreach (StackItem item in _array) + referenceCounter.AddReference(item, this); + } - /// - /// Add a new item at the end of the array. - /// - /// The item to be added. - public void Add(StackItem item) - { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); - _array.Add(item); - ReferenceCounter?.AddReference(item, this); - } + /// + /// Add a new item at the end of the array. + /// + /// The item to be added. + public void Add(StackItem item) + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + _array.Add(item); + ReferenceCounter?.AddReference(item, this); + } - public override void Clear() - { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); - if (ReferenceCounter != null) - foreach (StackItem item in _array) - ReferenceCounter.RemoveReference(item, this); - _array.Clear(); - } + public override void Clear() + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (ReferenceCounter != null) + foreach (StackItem item in _array) + ReferenceCounter.RemoveReference(item, this); + _array.Clear(); + } - public override StackItem ConvertTo(StackItemType type) - { - if (Type == StackItemType.Array && type == StackItemType.Struct) - return new Struct(ReferenceCounter, new List(_array)); - return base.ConvertTo(type); - } + public override StackItem ConvertTo(StackItemType type) + { + if (Type == StackItemType.Array && type == StackItemType.Struct) + return new Struct(ReferenceCounter, new List(_array)); + return base.ConvertTo(type); + } - internal sealed override StackItem DeepCopy(Dictionary refMap, bool asImmutable) - { - if (refMap.TryGetValue(this, out StackItem? mappedItem)) return mappedItem; - var result = this is TestStruct ? new TestStruct(ReferenceCounter) : new TestArray(ReferenceCounter); - refMap.Add(this, result); - foreach (StackItem item in _array) - result.Add(item.DeepCopy(refMap, asImmutable)); - result.IsReadOnly = true; - return result; - } + internal sealed override StackItem DeepCopy(Dictionary refMap, bool asImmutable) + { + if (refMap.TryGetValue(this, out StackItem? mappedItem)) return mappedItem; + var result = this is TestStruct ? new TestStruct(ReferenceCounter) : new TestArray(ReferenceCounter); + refMap.Add(this, result); + foreach (StackItem item in _array) + result.Add(item.DeepCopy(refMap, asImmutable)); + result.IsReadOnly = true; + return result; + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - public IEnumerator GetEnumerator() - { - return _array.GetEnumerator(); - } + public IEnumerator GetEnumerator() + { + return _array.GetEnumerator(); + } - /// - /// Remove the item at the specified index. - /// - /// The index of the item to be removed. - public void RemoveAt(int index) - { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); - ReferenceCounter?.RemoveReference(_array[index], this); - _array.RemoveAt(index); - } + /// + /// Remove the item at the specified index. + /// + /// The index of the item to be removed. + public void RemoveAt(int index) + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + ReferenceCounter?.RemoveReference(_array[index], this); + _array.RemoveAt(index); + } - /// - /// Reverse all items in the array. - /// - public void Reverse() - { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); - _array.Reverse(); + /// + /// Reverse all items in the array. + /// + public void Reverse() + { + if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + _array.Reverse(); + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/TestStruct.cs b/benchmarks/Neo.VM.Benchmarks/TestStruct.cs index f7dc2fcb64..24b32530b5 100644 --- a/benchmarks/Neo.VM.Benchmarks/TestStruct.cs +++ b/benchmarks/Neo.VM.Benchmarks/TestStruct.cs @@ -11,119 +11,120 @@ using Neo.VM.Types; -namespace Neo.VM.Benchmark; - -public class TestStruct : TestArray +namespace Neo.VM.Benchmark { - public override StackItemType Type => StackItemType.Struct; - - /// - /// Create a structure with the specified fields. - /// - /// The fields to be included in the structure. - public TestStruct(IEnumerable? fields = null) - : this(null, fields) + public class TestStruct : TestArray { - } + public override StackItemType Type => StackItemType.Struct; - /// - /// Create a structure with the specified fields. And make the structure use the specified . - /// - /// The to be used by this structure. - /// The fields to be included in the structure. - public TestStruct(IReferenceCounter? referenceCounter, IEnumerable? fields = null) - : base(referenceCounter, fields) - { - } + /// + /// Create a structure with the specified fields. + /// + /// The fields to be included in the structure. + public TestStruct(IEnumerable? fields = null) + : this(null, fields) + { + } - /// - /// Create a new structure with the same content as this structure. All nested structures will be copied by value. - /// - /// Execution engine limits - /// The copied structure. - public TestStruct Clone(ExecutionEngineLimits limits) - { - int count = (int)(limits.MaxStackSize - 1); - TestStruct result = new(ReferenceCounter); - Queue queue = new(); - queue.Enqueue(result); - queue.Enqueue(this); - while (queue.Count > 0) + /// + /// Create a structure with the specified fields. And make the structure use the specified . + /// + /// The to be used by this structure. + /// The fields to be included in the structure. + public TestStruct(IReferenceCounter? referenceCounter, IEnumerable? fields = null) + : base(referenceCounter, fields) + { + } + + /// + /// Create a new structure with the same content as this structure. All nested structures will be copied by value. + /// + /// Execution engine limits + /// The copied structure. + public TestStruct Clone(ExecutionEngineLimits limits) { - TestStruct a = queue.Dequeue(); - TestStruct b = queue.Dequeue(); - foreach (StackItem item in b) + int count = (int)(limits.MaxStackSize - 1); + TestStruct result = new(ReferenceCounter); + Queue queue = new(); + queue.Enqueue(result); + queue.Enqueue(this); + while (queue.Count > 0) { - count--; - if (count < 0) throw new InvalidOperationException("Beyond clone limits!"); - if (item is TestStruct sb) - { - TestStruct sa = new(ReferenceCounter); - a.Add(sa); - queue.Enqueue(sa); - queue.Enqueue(sb); - } - else + TestStruct a = queue.Dequeue(); + TestStruct b = queue.Dequeue(); + foreach (StackItem item in b) { - a.Add(item); + count--; + if (count < 0) throw new InvalidOperationException("Beyond clone limits!"); + if (item is TestStruct sb) + { + TestStruct sa = new(ReferenceCounter); + a.Add(sa); + queue.Enqueue(sa); + queue.Enqueue(sb); + } + else + { + a.Add(item); + } } } + return result; } - return result; - } - public override StackItem ConvertTo(StackItemType type) - { - if (type == StackItemType.Array) - return new TestArray(ReferenceCounter, new List(_array)); - return base.ConvertTo(type); - } + public override StackItem ConvertTo(StackItemType type) + { + if (type == StackItemType.Array) + return new TestArray(ReferenceCounter, new List(_array)); + return base.ConvertTo(type); + } - public override bool Equals(StackItem? other) - { - throw new NotSupportedException(); - } + public override bool Equals(StackItem? other) + { + throw new NotSupportedException(); + } - internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) - { - if (other is not TestStruct s) return false; - Stack stack1 = new(); - Stack stack2 = new(); - stack1.Push(this); - stack2.Push(s); - uint count = limits.MaxStackSize; - uint maxComparableSize = limits.MaxComparableSize; - while (stack1.Count > 0) + internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) { - if (count-- == 0) - throw new InvalidOperationException("Too many struct items to compare."); - StackItem a = stack1.Pop(); - StackItem b = stack2.Pop(); - if (a is ByteString byteString) - { - if (!byteString.Equals(b, ref maxComparableSize)) return false; - } - else + if (other is not TestStruct s) return false; + Stack stack1 = new(); + Stack stack2 = new(); + stack1.Push(this); + stack2.Push(s); + uint count = limits.MaxStackSize; + uint maxComparableSize = limits.MaxComparableSize; + while (stack1.Count > 0) { - if (maxComparableSize == 0) - throw new InvalidOperationException("The operand exceeds the maximum comparable size."); - maxComparableSize -= 1; - if (a is TestStruct sa) + if (count-- == 0) + throw new InvalidOperationException("Too many struct items to compare."); + StackItem a = stack1.Pop(); + StackItem b = stack2.Pop(); + if (a is ByteString byteString) { - if (ReferenceEquals(a, b)) continue; - if (b is not TestStruct sb) return false; - if (sa.Count != sb.Count) return false; - foreach (StackItem item in sa) - stack1.Push(item); - foreach (StackItem item in sb) - stack2.Push(item); + if (!byteString.Equals(b, ref maxComparableSize)) return false; } else { - if (!a.Equals(b)) return false; + if (maxComparableSize == 0) + throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + maxComparableSize -= 1; + if (a is TestStruct sa) + { + if (ReferenceEquals(a, b)) continue; + if (b is not TestStruct sb) return false; + if (sa.Count != sb.Count) return false; + foreach (StackItem item in sa) + stack1.Push(item); + foreach (StackItem item in sb) + stack2.Push(item); + } + else + { + if (!a.Equals(b)) return false; + } } } + return true; } - return true; } } diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs index f2d78fda6c..de71f3332d 100644 --- a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs @@ -14,97 +14,98 @@ using Array = Neo.VM.Types.Array; using Buffer = Neo.VM.Types.Buffer; -namespace Neo.VM.Benchmark; - -public class Benchmarks_Convert +namespace Neo.VM.Benchmark { - private Dictionary>? testItemsByType; - - [GlobalSetup] - public void Setup() + public class Benchmarks_Convert { - testItemsByType = CreateTestItemsByType(); - } + private Dictionary>? testItemsByType; - [Benchmark] - [ArgumentsSource(nameof(GetTypeConversionPairs))] - public void BenchConvertTo(StackItemType fromType, StackItemType toType) - { - if (testItemsByType is null) - throw new InvalidOperationException($"{nameof(testItemsByType)} not initialized"); + [GlobalSetup] + public void Setup() + { + testItemsByType = CreateTestItemsByType(); + } - foreach (var item in testItemsByType[fromType]) + [Benchmark] + [ArgumentsSource(nameof(GetTypeConversionPairs))] + public void BenchConvertTo(StackItemType fromType, StackItemType toType) { - try - { - _ = item.ConvertTo(toType); - } - catch (Exception) + if (testItemsByType is null) + throw new InvalidOperationException($"{nameof(testItemsByType)} not initialized"); + + foreach (var item in testItemsByType[fromType]) { - // Ignore invalid casts as they're expected for some conversions + try + { + _ = item.ConvertTo(toType); + } + catch (Exception) + { + // Ignore invalid casts as they're expected for some conversions + } } } - } - public IEnumerable GetTypeConversionPairs() - { - var types = (StackItemType[])Enum.GetValues(typeof(StackItemType)); - foreach (var fromType in types) + public IEnumerable GetTypeConversionPairs() { - foreach (var toType in types) + var types = (StackItemType[])Enum.GetValues(typeof(StackItemType)); + foreach (var fromType in types) { - yield return new object[] { fromType, toType }; + foreach (var toType in types) + { + yield return new object[] { fromType, toType }; + } } } - } - private Dictionary> CreateTestItemsByType() - { - var referenceCounter = new ReferenceCounter(); - var result = new Dictionary>(); - - foreach (StackItemType type in Enum.GetValues(typeof(StackItemType))) + private Dictionary> CreateTestItemsByType() { - result[type] = new List(); - } + var referenceCounter = new ReferenceCounter(); + var result = new Dictionary>(); - result[StackItemType.Boolean].Add(StackItem.True); - result[StackItemType.Boolean].Add(StackItem.False); + foreach (StackItemType type in Enum.GetValues(typeof(StackItemType))) + { + result[type] = new List(); + } - result[StackItemType.Integer].Add(new Integer(42)); - result[StackItemType.Integer].Add(new Integer(-1)); + result[StackItemType.Boolean].Add(StackItem.True); + result[StackItemType.Boolean].Add(StackItem.False); - result[StackItemType.ByteString].Add(new ByteString(new byte[] { 1, 2, 3 })); - result[StackItemType.ByteString].Add(new ByteString(new byte[] { 255, 0, 128 })); + result[StackItemType.Integer].Add(new Integer(42)); + result[StackItemType.Integer].Add(new Integer(-1)); - // Create a 128-byte buffer - var longBuffer = new byte[128]; - for (int i = 0; i < 128; i++) longBuffer[i] = (byte)(i % 256); - result[StackItemType.Buffer].Add(new Buffer(longBuffer)); - result[StackItemType.Buffer].Add(new Buffer(new byte[128])); // Another 128-byte buffer, all zeros + result[StackItemType.ByteString].Add(new ByteString(new byte[] { 1, 2, 3 })); + result[StackItemType.ByteString].Add(new ByteString(new byte[] { 255, 0, 128 })); - // Create an array with 10 items - var longArray = new Array(referenceCounter); - for (int i = 0; i < 10; i++) longArray.Add(new Integer(i)); - result[StackItemType.Array].Add(longArray); - result[StackItemType.Array].Add(new Array(referenceCounter) { StackItem.True, new ByteString(new byte[] { 3, 4, 5 }) }); + // Create a 128-byte buffer + var longBuffer = new byte[128]; + for (int i = 0; i < 128; i++) longBuffer[i] = (byte)(i % 256); + result[StackItemType.Buffer].Add(new Buffer(longBuffer)); + result[StackItemType.Buffer].Add(new Buffer(new byte[128])); // Another 128-byte buffer, all zeros - // Create a struct with 10 items - var longStruct = new Struct(referenceCounter); - for (int i = 0; i < 10; i++) longStruct.Add(new Integer(i * 10)); - result[StackItemType.Struct].Add(longStruct); - result[StackItemType.Struct].Add(new Struct(referenceCounter) { StackItem.False, new Buffer(new byte[] { 6, 7, 8 }) }); + // Create an array with 10 items + var longArray = new Array(referenceCounter); + for (int i = 0; i < 10; i++) longArray.Add(new Integer(i)); + result[StackItemType.Array].Add(longArray); + result[StackItemType.Array].Add(new Array(referenceCounter) { StackItem.True, new ByteString(new byte[] { 3, 4, 5 }) }); - // Create a map with 10 items - var longMap = new Map(referenceCounter); - for (int i = 0; i < 10; i++) longMap[new Integer(i)] = new ByteString(new byte[] { (byte)(i * 20) }); - result[StackItemType.Map].Add(longMap); - result[StackItemType.Map].Add(new Map(referenceCounter) { [new ByteString(new byte[] { 9 })] = StackItem.True }); + // Create a struct with 10 items + var longStruct = new Struct(referenceCounter); + for (int i = 0; i < 10; i++) longStruct.Add(new Integer(i * 10)); + result[StackItemType.Struct].Add(longStruct); + result[StackItemType.Struct].Add(new Struct(referenceCounter) { StackItem.False, new Buffer(new byte[] { 6, 7, 8 }) }); - result[StackItemType.InteropInterface].Add(new InteropInterface(new object())); - result[StackItemType.InteropInterface].Add(new InteropInterface("test string")); + // Create a map with 10 items + var longMap = new Map(referenceCounter); + for (int i = 0; i < 10; i++) longMap[new Integer(i)] = new ByteString(new byte[] { (byte)(i * 20) }); + result[StackItemType.Map].Add(longMap); + result[StackItemType.Map].Add(new Map(referenceCounter) { [new ByteString(new byte[] { 9 })] = StackItem.True }); - return result; + result[StackItemType.InteropInterface].Add(new InteropInterface(new object())); + result[StackItemType.InteropInterface].Add(new InteropInterface("test string")); + + return result; + } } } diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs index 64cc3d6988..4cef2e555f 100644 --- a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs @@ -12,111 +12,112 @@ using BenchmarkDotNet.Attributes; using Array = Neo.VM.Types.Array; -namespace Neo.VM.Benchmark; - -public class Benchmarks_DeepCopy +namespace Neo.VM.Benchmark { - public IEnumerable<(int Depth, int ElementsPerLevel)> ParamSource() + public class Benchmarks_DeepCopy { - int[] depths = [2, 4]; - int[] elementsPerLevel = [2, 4, 6]; - - foreach (var depth in depths) + public IEnumerable<(int Depth, int ElementsPerLevel)> ParamSource() { - foreach (var elements in elementsPerLevel) + int[] depths = [2, 4]; + int[] elementsPerLevel = [2, 4, 6]; + + foreach (var depth in depths) { - if (depth <= 8 || elements <= 2) + foreach (var elements in elementsPerLevel) { - yield return (depth, elements); + if (depth <= 8 || elements <= 2) + { + yield return (depth, elements); + } } } } - } - - [ParamsSource(nameof(ParamSource))] - public (int Depth, int ElementsPerLevel) Params; - - [Benchmark] - public void BenchNestedArrayDeepCopy() - { - var root = new Array(new ReferenceCounter()); - CreateNestedArray(root, Params.Depth, Params.ElementsPerLevel); - _ = root.DeepCopy(); - } - - [Benchmark] - public void BenchNestedArrayDeepCopyWithReferenceCounter() - { - var referenceCounter = new ReferenceCounter(); - var root = new Array(referenceCounter); - CreateNestedArray(root, Params.Depth, Params.ElementsPerLevel, referenceCounter); - _ = root.DeepCopy(); - } - [Benchmark] - public void BenchNestedTestArrayDeepCopy() - { - var root = new TestArray(new ReferenceCounter()); - CreateNestedTestArray(root, Params.Depth, Params.ElementsPerLevel); - _ = root.DeepCopy(); - } - - [Benchmark] - public void BenchNestedTestArrayDeepCopyWithReferenceCounter() - { - var referenceCounter = new ReferenceCounter(); - var root = new TestArray(referenceCounter); - CreateNestedTestArray(root, Params.Depth, Params.ElementsPerLevel, referenceCounter); - _ = root.DeepCopy(); - } + [ParamsSource(nameof(ParamSource))] + public (int Depth, int ElementsPerLevel) Params; - private static void CreateNestedArray(Array? rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter? referenceCounter = null) - { - if (depth < 0) + [Benchmark] + public void BenchNestedArrayDeepCopy() { - throw new ArgumentException("Depth must be non-negative", nameof(depth)); + var root = new Array(new ReferenceCounter()); + CreateNestedArray(root, Params.Depth, Params.ElementsPerLevel); + _ = root.DeepCopy(); } - if (rootArray == null) + [Benchmark] + public void BenchNestedArrayDeepCopyWithReferenceCounter() { - throw new ArgumentNullException(nameof(rootArray)); + var referenceCounter = new ReferenceCounter(); + var root = new Array(referenceCounter); + CreateNestedArray(root, Params.Depth, Params.ElementsPerLevel, referenceCounter); + _ = root.DeepCopy(); } - if (depth == 0) + [Benchmark] + public void BenchNestedTestArrayDeepCopy() { - return; + var root = new TestArray(new ReferenceCounter()); + CreateNestedTestArray(root, Params.Depth, Params.ElementsPerLevel); + _ = root.DeepCopy(); } - for (var i = 0; i < elementsPerLevel; i++) + [Benchmark] + public void BenchNestedTestArrayDeepCopyWithReferenceCounter() { - var childArray = new Array(referenceCounter); - rootArray.Add(childArray); - CreateNestedArray(childArray, depth - 1, elementsPerLevel, referenceCounter); + var referenceCounter = new ReferenceCounter(); + var root = new TestArray(referenceCounter); + CreateNestedTestArray(root, Params.Depth, Params.ElementsPerLevel, referenceCounter); + _ = root.DeepCopy(); } - } - private static void CreateNestedTestArray(TestArray rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter? referenceCounter = null) - { - if (depth < 0) + private static void CreateNestedArray(Array? rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter? referenceCounter = null) { - throw new ArgumentException("Depth must be non-negative", nameof(depth)); - } + if (depth < 0) + { + throw new ArgumentException("Depth must be non-negative", nameof(depth)); + } - if (rootArray == null) - { - throw new ArgumentNullException(nameof(rootArray)); - } + if (rootArray == null) + { + throw new ArgumentNullException(nameof(rootArray)); + } - if (depth == 0) - { - return; + if (depth == 0) + { + return; + } + + for (var i = 0; i < elementsPerLevel; i++) + { + var childArray = new Array(referenceCounter); + rootArray.Add(childArray); + CreateNestedArray(childArray, depth - 1, elementsPerLevel, referenceCounter); + } } - for (var i = 0; i < elementsPerLevel; i++) + private static void CreateNestedTestArray(TestArray rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter? referenceCounter = null) { - var childArray = new TestArray(referenceCounter); - rootArray.Add(childArray); - CreateNestedTestArray(childArray, depth - 1, elementsPerLevel, referenceCounter); + if (depth < 0) + { + throw new ArgumentException("Depth must be non-negative", nameof(depth)); + } + + if (rootArray == null) + { + throw new ArgumentNullException(nameof(rootArray)); + } + + if (depth == 0) + { + return; + } + + for (var i = 0; i < elementsPerLevel; i++) + { + var childArray = new TestArray(referenceCounter); + rootArray.Add(childArray); + CreateNestedTestArray(childArray, depth - 1, elementsPerLevel, referenceCounter); + } } } } diff --git a/global.json b/global.json index beefe210a1..41c9ad2ed8 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.202", + "version": "9.0.100", "rollForward": "latestFeature", "allowPrerelease": false } diff --git a/scripts/Neo.CLI/test-neo-cli.exp b/scripts/Neo.CLI/test-neo-cli.exp index e7124402c3..73705c8a15 100644 --- a/scripts/Neo.CLI/test-neo-cli.exp +++ b/scripts/Neo.CLI/test-neo-cli.exp @@ -6,7 +6,7 @@ set timeout 10 exp_internal true # Start neo-cli -spawn dotnet ./bin/Neo.CLI/net8.0/neo-cli.dll +spawn dotnet ./bin/Neo.CLI/net9.0/neo-cli.dll # Expect the main input prompt expect { diff --git a/src/Neo.CLI/CLI/MainService.cs b/src/Neo.CLI/CLI/MainService.cs index 96db57a19d..266549f3cf 100644 --- a/src/Neo.CLI/CLI/MainService.cs +++ b/src/Neo.CLI/CLI/MainService.cs @@ -521,13 +521,13 @@ private void WriteBlocks(uint start, uint count, string path, bool writeStart) if (writeStart) { fs.Seek(sizeof(uint), SeekOrigin.Begin); - fs.Read(buffer, 0, buffer.Length); + fs.ReadExactly(buffer); start += BitConverter.ToUInt32(buffer, 0); fs.Seek(sizeof(uint), SeekOrigin.Begin); } else { - fs.Read(buffer, 0, buffer.Length); + fs.ReadExactly(buffer); start = BitConverter.ToUInt32(buffer, 0); fs.Seek(0, SeekOrigin.Begin); } diff --git a/src/Neo.CLI/Dockerfile b/src/Neo.CLI/Dockerfile index 5863e9a74d..100056b6f7 100644 --- a/src/Neo.CLI/Dockerfile +++ b/src/Neo.CLI/Dockerfile @@ -1,13 +1,13 @@ -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS Build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS Build # Run this from the repository root folder COPY src . COPY NuGet.Config /Neo.CLI WORKDIR /Neo.CLI -RUN dotnet restore && dotnet publish -f net8.0 -c Release -o /app +RUN dotnet restore && dotnet publish -f net9.0 -c Release -o /app -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 AS Final +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:9.0 AS Final RUN apt-get update && apt-get install -y \ screen \ libleveldb-dev \ diff --git a/src/Neo.CLI/Neo.CLI.csproj b/src/Neo.CLI/Neo.CLI.csproj index f2e1359194..ff71de843f 100644 --- a/src/Neo.CLI/Neo.CLI.csproj +++ b/src/Neo.CLI/Neo.CLI.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.CLI neo-cli Exe diff --git a/src/Neo.ConsoleService/Neo.ConsoleService.csproj b/src/Neo.ConsoleService/Neo.ConsoleService.csproj index b4254a4cad..51c90d9520 100644 --- a/src/Neo.ConsoleService/Neo.ConsoleService.csproj +++ b/src/Neo.ConsoleService/Neo.ConsoleService.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0 + netstandard2.1;net9.0 Neo.ConsoleService enable ../../bin/$(PackageId) @@ -9,7 +9,7 @@ - + diff --git a/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs b/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs index 3981f19a1b..3d8ba652f4 100644 --- a/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs +++ b/src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs @@ -12,60 +12,61 @@ using System.Runtime.CompilerServices; using static Neo.Cryptography.BLS12_381.MillerLoopUtility; -namespace Neo.Cryptography.BLS12_381; - -partial class Bls12 +namespace Neo.Cryptography.BLS12_381 { - class Adder : IMillerLoopDriver + partial class Bls12 { - public G2Projective Curve; - public readonly G2Affine Base; - public readonly G1Affine P; - - public Adder(in G1Affine p, in G2Affine q) + class Adder : IMillerLoopDriver { - Curve = new(q); - Base = q; - P = p; - } + public G2Projective Curve; + public readonly G2Affine Base; + public readonly G1Affine P; - Fp12 IMillerLoopDriver.DoublingStep(in Fp12 f) - { - var coeffs = DoublingStep(ref Curve); - return Ell(in f, in coeffs, in P); - } + public Adder(in G1Affine p, in G2Affine q) + { + Curve = new(q); + Base = q; + P = p; + } - Fp12 IMillerLoopDriver.AdditionStep(in Fp12 f) - { - var coeffs = AdditionStep(ref Curve, in Base); - return Ell(in f, in coeffs, in P); - } + Fp12 IMillerLoopDriver.DoublingStep(in Fp12 f) + { + var coeffs = DoublingStep(ref Curve); + return Ell(in f, in coeffs, in P); + } - #region IMillerLoopDriver + Fp12 IMillerLoopDriver.AdditionStep(in Fp12 f) + { + var coeffs = AdditionStep(ref Curve, in Base); + return Ell(in f, in coeffs, in P); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Fp12 Square(in Fp12 f) => f.Square(); + #region IMillerLoopDriver - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Fp12 Conjugate(in Fp12 f) => f.Conjugate(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp12 Square(in Fp12 f) => f.Square(); - public static Fp12 One - { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Fp12.One; - } + public static Fp12 Conjugate(in Fp12 f) => f.Conjugate(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - Fp12 IMillerLoopDriver.Square(in Fp12 f) => Adder.Square(f); + public static Fp12 One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Fp12.One; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - Fp12 IMillerLoopDriver.Conjugate(in Fp12 f) => Adder.Conjugate(f); - Fp12 IMillerLoopDriver.One - { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Adder.One; - } + Fp12 IMillerLoopDriver.Square(in Fp12 f) => Adder.Square(f); - #endregion + [MethodImpl(MethodImplOptions.AggressiveInlining)] + Fp12 IMillerLoopDriver.Conjugate(in Fp12 f) => Adder.Conjugate(f); + Fp12 IMillerLoopDriver.One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Adder.One; + } + + #endregion + } } } diff --git a/src/Neo.Cryptography.BLS12_381/Bls12.cs b/src/Neo.Cryptography.BLS12_381/Bls12.cs index 10022ded4d..0b4727e0cc 100644 --- a/src/Neo.Cryptography.BLS12_381/Bls12.cs +++ b/src/Neo.Cryptography.BLS12_381/Bls12.cs @@ -12,20 +12,21 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.MillerLoopUtility; -namespace Neo.Cryptography.BLS12_381; - -public static partial class Bls12 +namespace Neo.Cryptography.BLS12_381 { - public static Gt Pairing(in G1Affine p, in G2Affine q) + public static partial class Bls12 { - var either_identity = p.IsIdentity | q.IsIdentity; - var p2 = ConditionalSelect(in p, in G1Affine.Generator, either_identity); - var q2 = ConditionalSelect(in q, in G2Affine.Generator, either_identity); + public static Gt Pairing(in G1Affine p, in G2Affine q) + { + var either_identity = p.IsIdentity | q.IsIdentity; + var p2 = ConditionalSelect(in p, in G1Affine.Generator, either_identity); + var q2 = ConditionalSelect(in q, in G2Affine.Generator, either_identity); - var adder = new Adder(p2, q2); + var adder = new Adder(p2, q2); - var tmp = MillerLoop(adder); - var tmp2 = new MillerLoopResult(ConditionalSelect(in tmp, in Fp12.One, either_identity)); - return tmp2.FinalExponentiation(); + var tmp = MillerLoop(adder); + var tmp2 = new MillerLoopResult(ConditionalSelect(in tmp, in Fp12.One, either_identity)); + return tmp2.FinalExponentiation(); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs b/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs index 3bfc1226e6..096fd9b3c9 100644 --- a/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs +++ b/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs @@ -13,25 +13,26 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -namespace Neo.Cryptography.BLS12_381; - -public static class ConstantTimeUtility +namespace Neo.Cryptography.BLS12_381 { - public static bool ConstantTimeEq(in T a, in T b) where T : unmanaged + public static class ConstantTimeUtility { - var a_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in a), 1)); - var b_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in b), 1)); + public static bool ConstantTimeEq(in T a, in T b) where T : unmanaged + { + var a_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in a), 1)); + var b_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in b), 1)); - return CryptographicOperations.FixedTimeEquals(a_bytes, b_bytes); - } + return CryptographicOperations.FixedTimeEquals(a_bytes, b_bytes); + } - public static T ConditionalSelect(in T a, in T b, bool choice) where T : unmanaged - { - return choice ? b : a; - } + public static T ConditionalSelect(in T a, in T b, bool choice) where T : unmanaged + { + return choice ? b : a; + } - public static void ConditionalAssign(this ref T self, in T other, bool choice) where T : unmanaged - { - self = ConditionalSelect(in self, in other, choice); + public static void ConditionalAssign(this ref T self, in T other, bool choice) where T : unmanaged + { + self = ConditionalSelect(in self, in other, choice); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/Constants.cs b/src/Neo.Cryptography.BLS12_381/Constants.cs index 457b4d720b..1a4efdc1f5 100644 --- a/src/Neo.Cryptography.BLS12_381/Constants.cs +++ b/src/Neo.Cryptography.BLS12_381/Constants.cs @@ -9,10 +9,11 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class Constants +namespace Neo.Cryptography.BLS12_381 { - public const ulong BLS_X = 0xd201_0000_0001_0000; - public const bool BLS_X_IS_NEGATIVE = true; + static class Constants + { + public const ulong BLS_X = 0xd201_0000_0001_0000; + public const bool BLS_X_IS_NEGATIVE = true; + } } diff --git a/src/Neo.Cryptography.BLS12_381/Fp.cs b/src/Neo.Cryptography.BLS12_381/Fp.cs index 4b30b4fa5c..f847630f6f 100644 --- a/src/Neo.Cryptography.BLS12_381/Fp.cs +++ b/src/Neo.Cryptography.BLS12_381/Fp.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -18,474 +19,471 @@ using static Neo.Cryptography.BLS12_381.FpConstants; using static Neo.Cryptography.BLS12_381.MathUtility; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Size)] -public readonly struct Fp : IEquatable, INumber +namespace Neo.Cryptography.BLS12_381 { - public const int Size = 48; - public const int SizeL = Size / sizeof(ulong); + [StructLayout(LayoutKind.Explicit, Size = Size)] + public readonly struct Fp : IEquatable, INumber + { + public const int Size = 48; + public const int SizeL = Size / sizeof(ulong); - private static readonly Fp _zero = new(); + private static readonly Fp _zero = new(); - public static ref readonly Fp Zero => ref _zero; - public static ref readonly Fp One => ref R; + public static ref readonly Fp Zero => ref _zero; + public static ref readonly Fp One => ref R; - public bool IsZero => this == Zero; + public bool IsZero => this == Zero; - public static Fp FromBytes(ReadOnlySpan data) - { - if (data.Length != Size) - throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); - - Span tmp = stackalloc ulong[SizeL]; - BinaryPrimitives.TryReadUInt64BigEndian(data[0..8], out tmp[5]); - BinaryPrimitives.TryReadUInt64BigEndian(data[8..16], out tmp[4]); - BinaryPrimitives.TryReadUInt64BigEndian(data[16..24], out tmp[3]); - BinaryPrimitives.TryReadUInt64BigEndian(data[24..32], out tmp[2]); - BinaryPrimitives.TryReadUInt64BigEndian(data[32..40], out tmp[1]); - BinaryPrimitives.TryReadUInt64BigEndian(data[40..48], out tmp[0]); - ReadOnlySpan span = MemoryMarshal.Cast(tmp); - - try + public static Fp FromBytes(ReadOnlySpan data) { - return span[0] * R2; - } - finally - { - ulong borrow; - (_, borrow) = Sbb(tmp[0], MODULUS[0], 0); - (_, borrow) = Sbb(tmp[1], MODULUS[1], borrow); - (_, borrow) = Sbb(tmp[2], MODULUS[2], borrow); - (_, borrow) = Sbb(tmp[3], MODULUS[3], borrow); - (_, borrow) = Sbb(tmp[4], MODULUS[4], borrow); - (_, borrow) = Sbb(tmp[5], MODULUS[5], borrow); - if (borrow == 0) + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + + Span tmp = stackalloc ulong[SizeL]; + BinaryPrimitives.TryReadUInt64BigEndian(data[0..8], out tmp[5]); + BinaryPrimitives.TryReadUInt64BigEndian(data[8..16], out tmp[4]); + BinaryPrimitives.TryReadUInt64BigEndian(data[16..24], out tmp[3]); + BinaryPrimitives.TryReadUInt64BigEndian(data[24..32], out tmp[2]); + BinaryPrimitives.TryReadUInt64BigEndian(data[32..40], out tmp[1]); + BinaryPrimitives.TryReadUInt64BigEndian(data[40..48], out tmp[0]); + ReadOnlySpan span = MemoryMarshal.Cast(tmp); + + try + { + return span[0] * R2; + } + finally { - // If the element is smaller than MODULUS then the subtraction will underflow. - // Otherwise, throws. - // Why not throw before return? - // Because we want to run the method in a constant time. - throw new FormatException(); + ulong borrow; + (_, borrow) = Sbb(tmp[0], MODULUS[0], 0); + (_, borrow) = Sbb(tmp[1], MODULUS[1], borrow); + (_, borrow) = Sbb(tmp[2], MODULUS[2], borrow); + (_, borrow) = Sbb(tmp[3], MODULUS[3], borrow); + (_, borrow) = Sbb(tmp[4], MODULUS[4], borrow); + (_, borrow) = Sbb(tmp[5], MODULUS[5], borrow); + if (borrow == 0) + { + // If the element is smaller than MODULUS then the subtraction will underflow. + // Otherwise, throws. + // Why not throw before return? + // Because we want to run the method in a constant time. + throw new FormatException(); + } } } - } - internal static Fp FromRawUnchecked(ulong[] values) - { - if (values.Length != SizeL) - throw new FormatException($"The argument `{nameof(values)}` must contain {SizeL} entries."); + internal static Fp FromRawUnchecked(ulong[] values) + { + if (values.Length != SizeL) + throw new FormatException($"The argument `{nameof(values)}` must contain {SizeL} entries."); - return MemoryMarshal.Cast(values)[0]; - } + return MemoryMarshal.Cast(values)[0]; + } - public static Fp Random(RandomNumberGenerator rng) - { - Span buffer = stackalloc byte[Size * 2]; - rng.GetBytes(buffer); - Span d = MemoryMarshal.Cast(buffer); - return d[0] * R2 + d[1] * R3; - } + public static Fp Random(RandomNumberGenerator rng) + { + Span buffer = stackalloc byte[Size * 2]; + rng.GetBytes(buffer); + Span d = MemoryMarshal.Cast(buffer); + return d[0] * R2 + d[1] * R3; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReadOnlySpan GetSpan() - { - return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan GetSpan() + { + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Span GetSpanU64() - { - return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetSpanU64() + { + return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); + } - public static bool operator ==(in Fp left, in Fp right) - { - return ConstantTimeEq(in left, in right); - } + public static bool operator ==(in Fp left, in Fp right) + { + return ConstantTimeEq(in left, in right); + } - public static bool operator !=(in Fp left, in Fp right) - { - return !(left == right); - } + public static bool operator !=(in Fp left, in Fp right) + { + return !(left == right); + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not Fp other) return false; - return this == other; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp other) return false; + return this == other; + } - public bool Equals(Fp other) - { - return this == other; - } + public bool Equals(Fp other) + { + return this == other; + } - public override int GetHashCode() - { - return base.GetHashCode(); - } + public override int GetHashCode() + { + return base.GetHashCode(); + } - public byte[] ToArray() - { - byte[] result = new byte[Size]; - TryWrite(result); - return result; - } + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } - public bool TryWrite(Span buffer) - { - if (buffer.Length < Size) return false; + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; - ReadOnlySpan u64 = GetSpanU64(); - Fp tmp = MontgomeryReduce(u64[0], u64[1], u64[2], u64[3], u64[4], u64[5], 0, 0, 0, 0, 0, 0); - u64 = tmp.GetSpanU64(); + ReadOnlySpan u64 = GetSpanU64(); + Fp tmp = MontgomeryReduce(u64[0], u64[1], u64[2], u64[3], u64[4], u64[5], 0, 0, 0, 0, 0, 0); + u64 = tmp.GetSpanU64(); - BinaryPrimitives.WriteUInt64BigEndian(buffer[0..8], u64[5]); - BinaryPrimitives.WriteUInt64BigEndian(buffer[8..16], u64[4]); - BinaryPrimitives.WriteUInt64BigEndian(buffer[16..24], u64[3]); - BinaryPrimitives.WriteUInt64BigEndian(buffer[24..32], u64[2]); - BinaryPrimitives.WriteUInt64BigEndian(buffer[32..40], u64[1]); - BinaryPrimitives.WriteUInt64BigEndian(buffer[40..48], u64[0]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[0..8], u64[5]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[8..16], u64[4]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[16..24], u64[3]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[24..32], u64[2]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[32..40], u64[1]); + BinaryPrimitives.WriteUInt64BigEndian(buffer[40..48], u64[0]); - return true; - } + return true; + } - public override string ToString() - { - var output = string.Empty; - foreach (var b in ToArray()) - output += b.ToString("x2"); + public override string ToString() + { + return "0x" + ToArray().ToHexString(); + } - return "0x" + output; - } + public bool LexicographicallyLargest() + { + ReadOnlySpan s = GetSpanU64(); + Fp tmp = MontgomeryReduce(s[0], s[1], s[2], s[3], s[4], s[5], 0, 0, 0, 0, 0, 0); + ReadOnlySpan t = tmp.GetSpanU64(); + ulong borrow; - public bool LexicographicallyLargest() - { - ReadOnlySpan s = GetSpanU64(); - Fp tmp = MontgomeryReduce(s[0], s[1], s[2], s[3], s[4], s[5], 0, 0, 0, 0, 0, 0); - ReadOnlySpan t = tmp.GetSpanU64(); - ulong borrow; - - (_, borrow) = Sbb(t[0], 0xdcff_7fff_ffff_d556, 0); - (_, borrow) = Sbb(t[1], 0x0f55_ffff_58a9_ffff, borrow); - (_, borrow) = Sbb(t[2], 0xb398_6950_7b58_7b12, borrow); - (_, borrow) = Sbb(t[3], 0xb23b_a5c2_79c2_895f, borrow); - (_, borrow) = Sbb(t[4], 0x258d_d3db_21a5_d66b, borrow); - (_, borrow) = Sbb(t[5], 0x0d00_88f5_1cbf_f34d, borrow); - - return borrow == 0; - } + (_, borrow) = Sbb(t[0], 0xdcff_7fff_ffff_d556, 0); + (_, borrow) = Sbb(t[1], 0x0f55_ffff_58a9_ffff, borrow); + (_, borrow) = Sbb(t[2], 0xb398_6950_7b58_7b12, borrow); + (_, borrow) = Sbb(t[3], 0xb23b_a5c2_79c2_895f, borrow); + (_, borrow) = Sbb(t[4], 0x258d_d3db_21a5_d66b, borrow); + (_, borrow) = Sbb(t[5], 0x0d00_88f5_1cbf_f34d, borrow); - public Fp Sqrt() - { - // We use Shank's method, as p = 3 (mod 4). This means - // we only need to exponentiate by (p + 1) / 4. This only - // works for elements that are actually quadratic residue, - // so we check that we got the correct result at the end. - Fp result = this.PowVartime(P_1_4); - if (result.Square() != this) throw new ArithmeticException(); - return result; - } + return borrow == 0; + } - public Fp Invert() - { - if (!TryInvert(out Fp result)) - throw new DivideByZeroException(); - return result; - } + public Fp Sqrt() + { + // We use Shank's method, as p = 3 (mod 4). This means + // we only need to exponentiate by (p + 1) / 4. This only + // works for elements that are actually quadratic residue, + // so we check that we got the correct result at the end. + Fp result = this.PowVartime(P_1_4); + if (result.Square() != this) throw new ArithmeticException(); + return result; + } - public bool TryInvert(out Fp result) - { - // Exponentiate by p - 2 - result = this.PowVartime(P_2); + public Fp Invert() + { + if (!TryInvert(out Fp result)) + throw new DivideByZeroException(); + return result; + } - // Why not return before Pow() if IsZero? - // Because we want to run the method in a constant time. - return !IsZero; - } + public bool TryInvert(out Fp result) + { + // Exponentiate by p - 2 + result = this.PowVartime(P_2); - private Fp SubtractP() - { - Fp result; - ReadOnlySpan s = GetSpanU64(); - Span r = result.GetSpanU64(); - ulong borrow; - - (r[0], borrow) = Sbb(s[0], MODULUS[0], 0); - (r[1], borrow) = Sbb(s[1], MODULUS[1], borrow); - (r[2], borrow) = Sbb(s[2], MODULUS[2], borrow); - (r[3], borrow) = Sbb(s[3], MODULUS[3], borrow); - (r[4], borrow) = Sbb(s[4], MODULUS[4], borrow); - (r[5], borrow) = Sbb(s[5], MODULUS[5], borrow); - - borrow = borrow == 0 ? ulong.MinValue : ulong.MaxValue; - r[0] = (s[0] & borrow) | (r[0] & ~borrow); - r[1] = (s[1] & borrow) | (r[1] & ~borrow); - r[2] = (s[2] & borrow) | (r[2] & ~borrow); - r[3] = (s[3] & borrow) | (r[3] & ~borrow); - r[4] = (s[4] & borrow) | (r[4] & ~borrow); - r[5] = (s[5] & borrow) | (r[5] & ~borrow); - - return result; - } + // Why not return before Pow() if IsZero? + // Because we want to run the method in a constant time. + return !IsZero; + } - public static Fp operator +(in Fp a, in Fp b) - { - Fp result; - ReadOnlySpan s = a.GetSpanU64(), r = b.GetSpanU64(); - Span d = result.GetSpanU64(); - - ulong carry = 0; - (d[0], carry) = Adc(s[0], r[0], carry); - (d[1], carry) = Adc(s[1], r[1], carry); - (d[2], carry) = Adc(s[2], r[2], carry); - (d[3], carry) = Adc(s[3], r[3], carry); - (d[4], carry) = Adc(s[4], r[4], carry); - (d[5], _) = Adc(s[5], r[5], carry); - - return result.SubtractP(); - } + private Fp SubtractP() + { + Fp result; + ReadOnlySpan s = GetSpanU64(); + Span r = result.GetSpanU64(); + ulong borrow; - public static Fp operator -(in Fp a) - { - Fp result; - ReadOnlySpan self = a.GetSpanU64(); - Span d = result.GetSpanU64(); - - ulong borrow = 0; - (d[0], borrow) = Sbb(MODULUS[0], self[0], borrow); - (d[1], borrow) = Sbb(MODULUS[1], self[1], borrow); - (d[2], borrow) = Sbb(MODULUS[2], self[2], borrow); - (d[3], borrow) = Sbb(MODULUS[3], self[3], borrow); - (d[4], borrow) = Sbb(MODULUS[4], self[4], borrow); - (d[5], _) = Sbb(MODULUS[5], self[5], borrow); - - ulong mask = a.IsZero ? ulong.MinValue : ulong.MaxValue; - d[0] &= mask; - d[1] &= mask; - d[2] &= mask; - d[3] &= mask; - d[4] &= mask; - d[5] &= mask; - - return result; - } + (r[0], borrow) = Sbb(s[0], MODULUS[0], 0); + (r[1], borrow) = Sbb(s[1], MODULUS[1], borrow); + (r[2], borrow) = Sbb(s[2], MODULUS[2], borrow); + (r[3], borrow) = Sbb(s[3], MODULUS[3], borrow); + (r[4], borrow) = Sbb(s[4], MODULUS[4], borrow); + (r[5], borrow) = Sbb(s[5], MODULUS[5], borrow); + + borrow = borrow == 0 ? ulong.MinValue : ulong.MaxValue; + r[0] = (s[0] & borrow) | (r[0] & ~borrow); + r[1] = (s[1] & borrow) | (r[1] & ~borrow); + r[2] = (s[2] & borrow) | (r[2] & ~borrow); + r[3] = (s[3] & borrow) | (r[3] & ~borrow); + r[4] = (s[4] & borrow) | (r[4] & ~borrow); + r[5] = (s[5] & borrow) | (r[5] & ~borrow); + + return result; + } - public static Fp operator -(in Fp a, in Fp b) - { - return -b + a; - } + public static Fp operator +(in Fp a, in Fp b) + { + Fp result; + ReadOnlySpan s = a.GetSpanU64(), r = b.GetSpanU64(); + Span d = result.GetSpanU64(); + + ulong carry = 0; + (d[0], carry) = Adc(s[0], r[0], carry); + (d[1], carry) = Adc(s[1], r[1], carry); + (d[2], carry) = Adc(s[2], r[2], carry); + (d[3], carry) = Adc(s[3], r[3], carry); + (d[4], carry) = Adc(s[4], r[4], carry); + (d[5], _) = Adc(s[5], r[5], carry); + + return result.SubtractP(); + } - public static Fp SumOfProducts(ReadOnlySpan a, ReadOnlySpan b) - { - int length = a.Length; - if (length != b.Length) - throw new ArgumentException("The lengths of the two arrays must be the same."); + public static Fp operator -(in Fp a) + { + Fp result; + ReadOnlySpan self = a.GetSpanU64(); + Span d = result.GetSpanU64(); + + ulong borrow = 0; + (d[0], borrow) = Sbb(MODULUS[0], self[0], borrow); + (d[1], borrow) = Sbb(MODULUS[1], self[1], borrow); + (d[2], borrow) = Sbb(MODULUS[2], self[2], borrow); + (d[3], borrow) = Sbb(MODULUS[3], self[3], borrow); + (d[4], borrow) = Sbb(MODULUS[4], self[4], borrow); + (d[5], _) = Sbb(MODULUS[5], self[5], borrow); + + ulong mask = a.IsZero ? ulong.MinValue : ulong.MaxValue; + d[0] &= mask; + d[1] &= mask; + d[2] &= mask; + d[3] &= mask; + d[4] &= mask; + d[5] &= mask; + + return result; + } - Fp result; - ReadOnlySpan au = MemoryMarshal.Cast(a); - ReadOnlySpan bu = MemoryMarshal.Cast(b); - Span u = result.GetSpanU64(); + public static Fp operator -(in Fp a, in Fp b) + { + return -b + a; + } - for (int j = 0; j < 6; j++) + public static Fp SumOfProducts(ReadOnlySpan a, ReadOnlySpan b) { - ulong carry; + int length = a.Length; + if (length != b.Length) + throw new ArgumentException("The lengths of the two arrays must be the same."); - var (t0, t1, t2, t3, t4, t5, t6) = (u[0], u[1], u[2], u[3], u[4], u[5], 0ul); - for (int i = 0; i < length; i++) + Fp result; + ReadOnlySpan au = MemoryMarshal.Cast(a); + ReadOnlySpan bu = MemoryMarshal.Cast(b); + Span u = result.GetSpanU64(); + + for (int j = 0; j < 6; j++) { - (t0, carry) = Mac(t0, au[i * SizeL + j], bu[i * SizeL + 0], 0); - (t1, carry) = Mac(t1, au[i * SizeL + j], bu[i * SizeL + 1], carry); - (t2, carry) = Mac(t2, au[i * SizeL + j], bu[i * SizeL + 2], carry); - (t3, carry) = Mac(t3, au[i * SizeL + j], bu[i * SizeL + 3], carry); - (t4, carry) = Mac(t4, au[i * SizeL + j], bu[i * SizeL + 4], carry); - (t5, carry) = Mac(t5, au[i * SizeL + j], bu[i * SizeL + 5], carry); - (t6, _) = Adc(t6, 0, carry); + ulong carry; + + var (t0, t1, t2, t3, t4, t5, t6) = (u[0], u[1], u[2], u[3], u[4], u[5], 0ul); + for (int i = 0; i < length; i++) + { + (t0, carry) = Mac(t0, au[i * SizeL + j], bu[i * SizeL + 0], 0); + (t1, carry) = Mac(t1, au[i * SizeL + j], bu[i * SizeL + 1], carry); + (t2, carry) = Mac(t2, au[i * SizeL + j], bu[i * SizeL + 2], carry); + (t3, carry) = Mac(t3, au[i * SizeL + j], bu[i * SizeL + 3], carry); + (t4, carry) = Mac(t4, au[i * SizeL + j], bu[i * SizeL + 4], carry); + (t5, carry) = Mac(t5, au[i * SizeL + j], bu[i * SizeL + 5], carry); + (t6, _) = Adc(t6, 0, carry); + } + + ulong k = unchecked(t0 * INV); + (_, carry) = Mac(t0, k, MODULUS[0], 0); + (u[0], carry) = Mac(t1, k, MODULUS[1], carry); + (u[1], carry) = Mac(t2, k, MODULUS[2], carry); + (u[2], carry) = Mac(t3, k, MODULUS[3], carry); + (u[3], carry) = Mac(t4, k, MODULUS[4], carry); + (u[4], carry) = Mac(t5, k, MODULUS[5], carry); + (u[5], _) = Adc(t6, 0, carry); } - ulong k = unchecked(t0 * INV); - (_, carry) = Mac(t0, k, MODULUS[0], 0); - (u[0], carry) = Mac(t1, k, MODULUS[1], carry); - (u[1], carry) = Mac(t2, k, MODULUS[2], carry); - (u[2], carry) = Mac(t3, k, MODULUS[3], carry); - (u[3], carry) = Mac(t4, k, MODULUS[4], carry); - (u[4], carry) = Mac(t5, k, MODULUS[5], carry); - (u[5], _) = Adc(t6, 0, carry); + return result.SubtractP(); } - return result.SubtractP(); - } + private static Fp MontgomeryReduce(ulong r0, ulong r1, ulong r2, ulong r3, ulong r4, ulong r5, ulong r6, ulong r7, ulong r8, ulong r9, ulong r10, ulong r11) + { + ulong carry, carry2; + + ulong k = unchecked(r0 * INV); + (_, carry) = Mac(r0, k, MODULUS[0], 0); + (r1, carry) = Mac(r1, k, MODULUS[1], carry); + (r2, carry) = Mac(r2, k, MODULUS[2], carry); + (r3, carry) = Mac(r3, k, MODULUS[3], carry); + (r4, carry) = Mac(r4, k, MODULUS[4], carry); + (r5, carry) = Mac(r5, k, MODULUS[5], carry); + (r6, carry2) = Adc(r6, 0, carry); + + k = unchecked(r1 * INV); + (_, carry) = Mac(r1, k, MODULUS[0], 0); + (r2, carry) = Mac(r2, k, MODULUS[1], carry); + (r3, carry) = Mac(r3, k, MODULUS[2], carry); + (r4, carry) = Mac(r4, k, MODULUS[3], carry); + (r5, carry) = Mac(r5, k, MODULUS[4], carry); + (r6, carry) = Mac(r6, k, MODULUS[5], carry); + (r7, carry2) = Adc(r7, carry2, carry); + + k = unchecked(r2 * INV); + (_, carry) = Mac(r2, k, MODULUS[0], 0); + (r3, carry) = Mac(r3, k, MODULUS[1], carry); + (r4, carry) = Mac(r4, k, MODULUS[2], carry); + (r5, carry) = Mac(r5, k, MODULUS[3], carry); + (r6, carry) = Mac(r6, k, MODULUS[4], carry); + (r7, carry) = Mac(r7, k, MODULUS[5], carry); + (r8, carry2) = Adc(r8, carry2, carry); + + k = unchecked(r3 * INV); + (_, carry) = Mac(r3, k, MODULUS[0], 0); + (r4, carry) = Mac(r4, k, MODULUS[1], carry); + (r5, carry) = Mac(r5, k, MODULUS[2], carry); + (r6, carry) = Mac(r6, k, MODULUS[3], carry); + (r7, carry) = Mac(r7, k, MODULUS[4], carry); + (r8, carry) = Mac(r8, k, MODULUS[5], carry); + (r9, carry2) = Adc(r9, carry2, carry); + + k = unchecked(r4 * INV); + (_, carry) = Mac(r4, k, MODULUS[0], 0); + (r5, carry) = Mac(r5, k, MODULUS[1], carry); + (r6, carry) = Mac(r6, k, MODULUS[2], carry); + (r7, carry) = Mac(r7, k, MODULUS[3], carry); + (r8, carry) = Mac(r8, k, MODULUS[4], carry); + (r9, carry) = Mac(r9, k, MODULUS[5], carry); + (r10, carry2) = Adc(r10, carry2, carry); + + k = unchecked(r5 * INV); + (_, carry) = Mac(r5, k, MODULUS[0], 0); + (r6, carry) = Mac(r6, k, MODULUS[1], carry); + (r7, carry) = Mac(r7, k, MODULUS[2], carry); + (r8, carry) = Mac(r8, k, MODULUS[3], carry); + (r9, carry) = Mac(r9, k, MODULUS[4], carry); + (r10, carry) = Mac(r10, k, MODULUS[5], carry); + (r11, _) = Adc(r11, carry2, carry); + + ReadOnlySpan tmp = stackalloc[] { r6, r7, r8, r9, r10, r11 }; + return MemoryMarshal.Cast(tmp)[0].SubtractP(); + } - private static Fp MontgomeryReduce(ulong r0, ulong r1, ulong r2, ulong r3, ulong r4, ulong r5, ulong r6, ulong r7, ulong r8, ulong r9, ulong r10, ulong r11) - { - ulong carry, carry2; - - ulong k = unchecked(r0 * INV); - (_, carry) = Mac(r0, k, MODULUS[0], 0); - (r1, carry) = Mac(r1, k, MODULUS[1], carry); - (r2, carry) = Mac(r2, k, MODULUS[2], carry); - (r3, carry) = Mac(r3, k, MODULUS[3], carry); - (r4, carry) = Mac(r4, k, MODULUS[4], carry); - (r5, carry) = Mac(r5, k, MODULUS[5], carry); - (r6, carry2) = Adc(r6, 0, carry); - - k = unchecked(r1 * INV); - (_, carry) = Mac(r1, k, MODULUS[0], 0); - (r2, carry) = Mac(r2, k, MODULUS[1], carry); - (r3, carry) = Mac(r3, k, MODULUS[2], carry); - (r4, carry) = Mac(r4, k, MODULUS[3], carry); - (r5, carry) = Mac(r5, k, MODULUS[4], carry); - (r6, carry) = Mac(r6, k, MODULUS[5], carry); - (r7, carry2) = Adc(r7, carry2, carry); - - k = unchecked(r2 * INV); - (_, carry) = Mac(r2, k, MODULUS[0], 0); - (r3, carry) = Mac(r3, k, MODULUS[1], carry); - (r4, carry) = Mac(r4, k, MODULUS[2], carry); - (r5, carry) = Mac(r5, k, MODULUS[3], carry); - (r6, carry) = Mac(r6, k, MODULUS[4], carry); - (r7, carry) = Mac(r7, k, MODULUS[5], carry); - (r8, carry2) = Adc(r8, carry2, carry); - - k = unchecked(r3 * INV); - (_, carry) = Mac(r3, k, MODULUS[0], 0); - (r4, carry) = Mac(r4, k, MODULUS[1], carry); - (r5, carry) = Mac(r5, k, MODULUS[2], carry); - (r6, carry) = Mac(r6, k, MODULUS[3], carry); - (r7, carry) = Mac(r7, k, MODULUS[4], carry); - (r8, carry) = Mac(r8, k, MODULUS[5], carry); - (r9, carry2) = Adc(r9, carry2, carry); - - k = unchecked(r4 * INV); - (_, carry) = Mac(r4, k, MODULUS[0], 0); - (r5, carry) = Mac(r5, k, MODULUS[1], carry); - (r6, carry) = Mac(r6, k, MODULUS[2], carry); - (r7, carry) = Mac(r7, k, MODULUS[3], carry); - (r8, carry) = Mac(r8, k, MODULUS[4], carry); - (r9, carry) = Mac(r9, k, MODULUS[5], carry); - (r10, carry2) = Adc(r10, carry2, carry); - - k = unchecked(r5 * INV); - (_, carry) = Mac(r5, k, MODULUS[0], 0); - (r6, carry) = Mac(r6, k, MODULUS[1], carry); - (r7, carry) = Mac(r7, k, MODULUS[2], carry); - (r8, carry) = Mac(r8, k, MODULUS[3], carry); - (r9, carry) = Mac(r9, k, MODULUS[4], carry); - (r10, carry) = Mac(r10, k, MODULUS[5], carry); - (r11, _) = Adc(r11, carry2, carry); - - ReadOnlySpan tmp = stackalloc[] { r6, r7, r8, r9, r10, r11 }; - return MemoryMarshal.Cast(tmp)[0].SubtractP(); - } + public static Fp operator *(in Fp a, in Fp b) + { + ReadOnlySpan s = a.GetSpanU64(), r = b.GetSpanU64(); + Span t = stackalloc ulong[SizeL * 2]; + ulong carry; - public static Fp operator *(in Fp a, in Fp b) - { - ReadOnlySpan s = a.GetSpanU64(), r = b.GetSpanU64(); - Span t = stackalloc ulong[SizeL * 2]; - ulong carry; - - (t[0], carry) = Mac(0, s[0], r[0], 0); - (t[1], carry) = Mac(0, s[0], r[1], carry); - (t[2], carry) = Mac(0, s[0], r[2], carry); - (t[3], carry) = Mac(0, s[0], r[3], carry); - (t[4], carry) = Mac(0, s[0], r[4], carry); - (t[5], t[6]) = Mac(0, s[0], r[5], carry); - - (t[1], carry) = Mac(t[1], s[1], r[0], 0); - (t[2], carry) = Mac(t[2], s[1], r[1], carry); - (t[3], carry) = Mac(t[3], s[1], r[2], carry); - (t[4], carry) = Mac(t[4], s[1], r[3], carry); - (t[5], carry) = Mac(t[5], s[1], r[4], carry); - (t[6], t[7]) = Mac(t[6], s[1], r[5], carry); - - (t[2], carry) = Mac(t[2], s[2], r[0], 0); - (t[3], carry) = Mac(t[3], s[2], r[1], carry); - (t[4], carry) = Mac(t[4], s[2], r[2], carry); - (t[5], carry) = Mac(t[5], s[2], r[3], carry); - (t[6], carry) = Mac(t[6], s[2], r[4], carry); - (t[7], t[8]) = Mac(t[7], s[2], r[5], carry); - (t[3], carry) = Mac(t[3], s[3], r[0], 0); - (t[4], carry) = Mac(t[4], s[3], r[1], carry); - (t[5], carry) = Mac(t[5], s[3], r[2], carry); - (t[6], carry) = Mac(t[6], s[3], r[3], carry); - (t[7], carry) = Mac(t[7], s[3], r[4], carry); - (t[8], t[9]) = Mac(t[8], s[3], r[5], carry); - (t[4], carry) = Mac(t[4], s[4], r[0], 0); - (t[5], carry) = Mac(t[5], s[4], r[1], carry); - (t[6], carry) = Mac(t[6], s[4], r[2], carry); - (t[7], carry) = Mac(t[7], s[4], r[3], carry); - (t[8], carry) = Mac(t[8], s[4], r[4], carry); - (t[9], t[10]) = Mac(t[9], s[4], r[5], carry); - (t[5], carry) = Mac(t[5], s[5], r[0], 0); - (t[6], carry) = Mac(t[6], s[5], r[1], carry); - (t[7], carry) = Mac(t[7], s[5], r[2], carry); - (t[8], carry) = Mac(t[8], s[5], r[3], carry); - (t[9], carry) = Mac(t[9], s[5], r[4], carry); - (t[10], t[11]) = Mac(t[10], s[5], r[5], carry); - - return MontgomeryReduce(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11]); - } + (t[0], carry) = Mac(0, s[0], r[0], 0); + (t[1], carry) = Mac(0, s[0], r[1], carry); + (t[2], carry) = Mac(0, s[0], r[2], carry); + (t[3], carry) = Mac(0, s[0], r[3], carry); + (t[4], carry) = Mac(0, s[0], r[4], carry); + (t[5], t[6]) = Mac(0, s[0], r[5], carry); + + (t[1], carry) = Mac(t[1], s[1], r[0], 0); + (t[2], carry) = Mac(t[2], s[1], r[1], carry); + (t[3], carry) = Mac(t[3], s[1], r[2], carry); + (t[4], carry) = Mac(t[4], s[1], r[3], carry); + (t[5], carry) = Mac(t[5], s[1], r[4], carry); + (t[6], t[7]) = Mac(t[6], s[1], r[5], carry); + + (t[2], carry) = Mac(t[2], s[2], r[0], 0); + (t[3], carry) = Mac(t[3], s[2], r[1], carry); + (t[4], carry) = Mac(t[4], s[2], r[2], carry); + (t[5], carry) = Mac(t[5], s[2], r[3], carry); + (t[6], carry) = Mac(t[6], s[2], r[4], carry); + (t[7], t[8]) = Mac(t[7], s[2], r[5], carry); + (t[3], carry) = Mac(t[3], s[3], r[0], 0); + (t[4], carry) = Mac(t[4], s[3], r[1], carry); + (t[5], carry) = Mac(t[5], s[3], r[2], carry); + (t[6], carry) = Mac(t[6], s[3], r[3], carry); + (t[7], carry) = Mac(t[7], s[3], r[4], carry); + (t[8], t[9]) = Mac(t[8], s[3], r[5], carry); + (t[4], carry) = Mac(t[4], s[4], r[0], 0); + (t[5], carry) = Mac(t[5], s[4], r[1], carry); + (t[6], carry) = Mac(t[6], s[4], r[2], carry); + (t[7], carry) = Mac(t[7], s[4], r[3], carry); + (t[8], carry) = Mac(t[8], s[4], r[4], carry); + (t[9], t[10]) = Mac(t[9], s[4], r[5], carry); + (t[5], carry) = Mac(t[5], s[5], r[0], 0); + (t[6], carry) = Mac(t[6], s[5], r[1], carry); + (t[7], carry) = Mac(t[7], s[5], r[2], carry); + (t[8], carry) = Mac(t[8], s[5], r[3], carry); + (t[9], carry) = Mac(t[9], s[5], r[4], carry); + (t[10], t[11]) = Mac(t[10], s[5], r[5], carry); + + return MontgomeryReduce(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11]); + } - public Fp Square() - { - ReadOnlySpan self = GetSpanU64(); - ulong t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11; - ulong carry; - - (t1, carry) = Mac(0, self[0], self[1], 0); - (t2, carry) = Mac(0, self[0], self[2], carry); - (t3, carry) = Mac(0, self[0], self[3], carry); - (t4, carry) = Mac(0, self[0], self[4], carry); - (t5, t6) = Mac(0, self[0], self[5], carry); - - (t3, carry) = Mac(t3, self[1], self[2], 0); - (t4, carry) = Mac(t4, self[1], self[3], carry); - (t5, carry) = Mac(t5, self[1], self[4], carry); - (t6, t7) = Mac(t6, self[1], self[5], carry); - - (t5, carry) = Mac(t5, self[2], self[3], 0); - (t6, carry) = Mac(t6, self[2], self[4], carry); - (t7, t8) = Mac(t7, self[2], self[5], carry); - - (t7, carry) = Mac(t7, self[3], self[4], 0); - (t8, t9) = Mac(t8, self[3], self[5], carry); - - (t9, t10) = Mac(t9, self[4], self[5], 0); - - t11 = t10 >> 63; - t10 = (t10 << 1) | (t9 >> 63); - t9 = (t9 << 1) | (t8 >> 63); - t8 = (t8 << 1) | (t7 >> 63); - t7 = (t7 << 1) | (t6 >> 63); - t6 = (t6 << 1) | (t5 >> 63); - t5 = (t5 << 1) | (t4 >> 63); - t4 = (t4 << 1) | (t3 >> 63); - t3 = (t3 << 1) | (t2 >> 63); - t2 = (t2 << 1) | (t1 >> 63); - t1 <<= 1; - - (t0, carry) = Mac(0, self[0], self[0], 0); - (t1, carry) = Adc(t1, carry, 0); - (t2, carry) = Mac(t2, self[1], self[1], carry); - (t3, carry) = Adc(t3, carry, 0); - (t4, carry) = Mac(t4, self[2], self[2], carry); - (t5, carry) = Adc(t5, carry, 0); - (t6, carry) = Mac(t6, self[3], self[3], carry); - (t7, carry) = Adc(t7, carry, 0); - (t8, carry) = Mac(t8, self[4], self[4], carry); - (t9, carry) = Adc(t9, carry, 0); - (t10, carry) = Mac(t10, self[5], self[5], carry); - (t11, _) = Adc(t11, carry, 0); - - return MontgomeryReduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); - } + public Fp Square() + { + ReadOnlySpan self = GetSpanU64(); + ulong t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11; + ulong carry; - #region Instance math methods + (t1, carry) = Mac(0, self[0], self[1], 0); + (t2, carry) = Mac(0, self[0], self[2], carry); + (t3, carry) = Mac(0, self[0], self[3], carry); + (t4, carry) = Mac(0, self[0], self[4], carry); + (t5, t6) = Mac(0, self[0], self[5], carry); + + (t3, carry) = Mac(t3, self[1], self[2], 0); + (t4, carry) = Mac(t4, self[1], self[3], carry); + (t5, carry) = Mac(t5, self[1], self[4], carry); + (t6, t7) = Mac(t6, self[1], self[5], carry); + + (t5, carry) = Mac(t5, self[2], self[3], 0); + (t6, carry) = Mac(t6, self[2], self[4], carry); + (t7, t8) = Mac(t7, self[2], self[5], carry); + + (t7, carry) = Mac(t7, self[3], self[4], 0); + (t8, t9) = Mac(t8, self[3], self[5], carry); + + (t9, t10) = Mac(t9, self[4], self[5], 0); + + t11 = t10 >> 63; + t10 = (t10 << 1) | (t9 >> 63); + t9 = (t9 << 1) | (t8 >> 63); + t8 = (t8 << 1) | (t7 >> 63); + t7 = (t7 << 1) | (t6 >> 63); + t6 = (t6 << 1) | (t5 >> 63); + t5 = (t5 << 1) | (t4 >> 63); + t4 = (t4 << 1) | (t3 >> 63); + t3 = (t3 << 1) | (t2 >> 63); + t2 = (t2 << 1) | (t1 >> 63); + t1 <<= 1; + + (t0, carry) = Mac(0, self[0], self[0], 0); + (t1, carry) = Adc(t1, carry, 0); + (t2, carry) = Mac(t2, self[1], self[1], carry); + (t3, carry) = Adc(t3, carry, 0); + (t4, carry) = Mac(t4, self[2], self[2], carry); + (t5, carry) = Adc(t5, carry, 0); + (t6, carry) = Mac(t6, self[3], self[3], carry); + (t7, carry) = Adc(t7, carry, 0); + (t8, carry) = Mac(t8, self[4], self[4], carry); + (t9, carry) = Adc(t9, carry, 0); + (t10, carry) = Mac(t10, self[5], self[5], carry); + (t11, _) = Adc(t11, carry, 0); + + return MontgomeryReduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + #region Instance math methods - public Fp Negate() => -this; - public Fp Multiply(in Fp value) => this * value; - public Fp Sum(in Fp value) => this + value; - public Fp Subtract(in Fp value) => this - value; + public Fp Negate() => -this; + public Fp Multiply(in Fp value) => this * value; + public Fp Sum(in Fp value) => this + value; + public Fp Subtract(in Fp value) => this - value; - #endregion + #endregion + } } diff --git a/src/Neo.Cryptography.BLS12_381/Fp12.cs b/src/Neo.Cryptography.BLS12_381/Fp12.cs index 2ed7ee988a..887c49c18c 100644 --- a/src/Neo.Cryptography.BLS12_381/Fp12.cs +++ b/src/Neo.Cryptography.BLS12_381/Fp12.cs @@ -13,206 +13,207 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Size)] -public readonly struct Fp12 : IEquatable, INumber +namespace Neo.Cryptography.BLS12_381 { - [FieldOffset(0)] - public readonly Fp6 C0; - [FieldOffset(Fp6.Size)] - public readonly Fp6 C1; - - public const int Size = Fp6.Size * 2; - - private static readonly Fp12 _zero = new(); - private static readonly Fp12 _one = new(in Fp6.One); + [StructLayout(LayoutKind.Explicit, Size = Size)] + public readonly struct Fp12 : IEquatable, INumber + { + [FieldOffset(0)] + public readonly Fp6 C0; + [FieldOffset(Fp6.Size)] + public readonly Fp6 C1; - public static ref readonly Fp12 Zero => ref _zero; - public static ref readonly Fp12 One => ref _one; + public const int Size = Fp6.Size * 2; - public bool IsZero => C0.IsZero & C1.IsZero; + private static readonly Fp12 _zero = new(); + private static readonly Fp12 _one = new(in Fp6.One); - public Fp12(in Fp f) - : this(new Fp6(in f), in Fp6.Zero) - { - } + public static ref readonly Fp12 Zero => ref _zero; + public static ref readonly Fp12 One => ref _one; - public Fp12(in Fp2 f) - : this(new Fp6(in f), in Fp6.Zero) - { - } + public bool IsZero => C0.IsZero & C1.IsZero; - public Fp12(in Fp6 f) - : this(in f, in Fp6.Zero) - { - } - - public Fp12(in Fp6 c0, in Fp6 c1) - { - C0 = c0; - C1 = c1; - } + public Fp12(in Fp f) + : this(new Fp6(in f), in Fp6.Zero) + { + } - public static Fp12 FromBytes(ReadOnlySpan data) - { - if (data.Length != Size) - throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); - Fp6 c0 = Fp6.FromBytes(data[Fp6.Size..]); - Fp6 c1 = Fp6.FromBytes(data[..Fp6.Size]); - return new(in c0, in c1); - } + public Fp12(in Fp2 f) + : this(new Fp6(in f), in Fp6.Zero) + { + } - public static bool operator ==(in Fp12 a, in Fp12 b) - { - return a.C0 == b.C0 & a.C1 == b.C1; - } + public Fp12(in Fp6 f) + : this(in f, in Fp6.Zero) + { + } - public static bool operator !=(in Fp12 a, in Fp12 b) - { - return !(a == b); - } + public Fp12(in Fp6 c0, in Fp6 c1) + { + C0 = c0; + C1 = c1; + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not Fp12 other) return false; - return this == other; - } + public static Fp12 FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + Fp6 c0 = Fp6.FromBytes(data[Fp6.Size..]); + Fp6 c1 = Fp6.FromBytes(data[..Fp6.Size]); + return new(in c0, in c1); + } + + public static bool operator ==(in Fp12 a, in Fp12 b) + { + return a.C0 == b.C0 & a.C1 == b.C1; + } - public bool Equals(Fp12 other) - { - return this == other; - } + public static bool operator !=(in Fp12 a, in Fp12 b) + { + return !(a == b); + } - public override int GetHashCode() - { - return C0.GetHashCode() ^ C1.GetHashCode(); - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp12 other) return false; + return this == other; + } - public byte[] ToArray() - { - byte[] result = new byte[Size]; - TryWrite(result); - return result; - } + public bool Equals(Fp12 other) + { + return this == other; + } - public bool TryWrite(Span buffer) - { - if (buffer.Length < Size) return false; - C0.TryWrite(buffer[Fp6.Size..Size]); - C1.TryWrite(buffer[0..Fp6.Size]); - return true; - } + public override int GetHashCode() + { + return C0.GetHashCode() ^ C1.GetHashCode(); + } - public static Fp12 Random(RandomNumberGenerator rng) - { - return new(Fp6.Random(rng), Fp6.Random(rng)); - } + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } - internal Fp12 MulBy_014(in Fp2 c0, in Fp2 c1, in Fp2 c4) - { - var aa = C0.MulBy_01(in c0, in c1); - var bb = C1.MulBy_1(in c4); - var o = c1 + c4; - var _c1 = C1 + C0; - _c1 = _c1.MulBy_01(in c0, in o); - _c1 = _c1 - aa - bb; - var _c0 = bb; - _c0 = _c0.MulByNonresidue(); - _c0 += aa; - - return new Fp12(in _c0, in _c1); - } + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + C0.TryWrite(buffer[Fp6.Size..Size]); + C1.TryWrite(buffer[0..Fp6.Size]); + return true; + } - public Fp12 Conjugate() - { - return new Fp12(in C0, -C1); - } + public static Fp12 Random(RandomNumberGenerator rng) + { + return new(Fp6.Random(rng), Fp6.Random(rng)); + } - public Fp12 FrobeniusMap() - { - var c0 = C0.FrobeniusMap(); - var c1 = C1.FrobeniusMap(); + internal Fp12 MulBy_014(in Fp2 c0, in Fp2 c1, in Fp2 c4) + { + var aa = C0.MulBy_01(in c0, in c1); + var bb = C1.MulBy_1(in c4); + var o = c1 + c4; + var _c1 = C1 + C0; + _c1 = _c1.MulBy_01(in c0, in o); + _c1 = _c1 - aa - bb; + var _c0 = bb; + _c0 = _c0.MulByNonresidue(); + _c0 += aa; + + return new Fp12(in _c0, in _c1); + } + + public Fp12 Conjugate() + { + return new Fp12(in C0, -C1); + } - // c1 = c1 * (u + 1)^((p - 1) / 6) - c1 *= new Fp6(new Fp2( - Fp.FromRawUnchecked(new ulong[] + public Fp12 FrobeniusMap() + { + var c0 = C0.FrobeniusMap(); + var c1 = C1.FrobeniusMap(); + + // c1 = c1 * (u + 1)^((p - 1) / 6) + c1 *= new Fp6(new Fp2( + Fp.FromRawUnchecked(new ulong[] + { + 0x0708_9552_b319_d465, + 0xc669_5f92_b50a_8313, + 0x97e8_3ccc_d117_228f, + 0xa35b_aeca_b2dc_29ee, + 0x1ce3_93ea_5daa_ce4d, + 0x08f2_220f_b0fb_66eb + }), Fp.FromRawUnchecked(new ulong[] { - 0x0708_9552_b319_d465, - 0xc669_5f92_b50a_8313, - 0x97e8_3ccc_d117_228f, - 0xa35b_aeca_b2dc_29ee, - 0x1ce3_93ea_5daa_ce4d, - 0x08f2_220f_b0fb_66eb - }), Fp.FromRawUnchecked(new ulong[] - { - 0xb2f6_6aad_4ce5_d646, - 0x5842_a06b_fc49_7cec, - 0xcf48_95d4_2599_d394, - 0xc11b_9cba_40a8_e8d0, - 0x2e38_13cb_e5a0_de89, - 0x110e_efda_8884_7faf - }))); - - return new Fp12(in c0, in c1); - } - - public Fp12 Square() - { - var ab = C0 * C1; - var c0c1 = C0 + C1; - var c0 = C1.MulByNonresidue(); - c0 += C0; - c0 *= c0c1; - c0 -= ab; - var c1 = ab + ab; - c0 -= ab.MulByNonresidue(); - - return new Fp12(in c0, in c1); - } + 0xb2f6_6aad_4ce5_d646, + 0x5842_a06b_fc49_7cec, + 0xcf48_95d4_2599_d394, + 0xc11b_9cba_40a8_e8d0, + 0x2e38_13cb_e5a0_de89, + 0x110e_efda_8884_7faf + }))); + + return new Fp12(in c0, in c1); + } + + public Fp12 Square() + { + var ab = C0 * C1; + var c0c1 = C0 + C1; + var c0 = C1.MulByNonresidue(); + c0 += C0; + c0 *= c0c1; + c0 -= ab; + var c1 = ab + ab; + c0 -= ab.MulByNonresidue(); + + return new Fp12(in c0, in c1); + } + + public Fp12 Invert() + { + Fp6 t = (C0.Square() - C1.Square().MulByNonresidue()).Invert(); + return new Fp12(C0 * t, C1 * -t); + } - public Fp12 Invert() - { - Fp6 t = (C0.Square() - C1.Square().MulByNonresidue()).Invert(); - return new Fp12(C0 * t, C1 * -t); - } + public static Fp12 operator -(in Fp12 a) + { + return new Fp12(-a.C0, -a.C1); + } - public static Fp12 operator -(in Fp12 a) - { - return new Fp12(-a.C0, -a.C1); - } + public static Fp12 operator +(in Fp12 a, in Fp12 b) + { + return new Fp12(a.C0 + b.C0, a.C1 + b.C1); + } - public static Fp12 operator +(in Fp12 a, in Fp12 b) - { - return new Fp12(a.C0 + b.C0, a.C1 + b.C1); - } + public static Fp12 operator -(in Fp12 a, in Fp12 b) + { + return new Fp12(a.C0 - b.C0, a.C1 - b.C1); + } - public static Fp12 operator -(in Fp12 a, in Fp12 b) - { - return new Fp12(a.C0 - b.C0, a.C1 - b.C1); - } + public static Fp12 operator *(in Fp12 a, in Fp12 b) + { + var aa = a.C0 * b.C0; + var bb = a.C1 * b.C1; + var o = b.C0 + b.C1; + var c1 = a.C1 + a.C0; + c1 *= o; + c1 -= aa; + c1 -= bb; + var c0 = bb.MulByNonresidue(); + c0 += aa; - public static Fp12 operator *(in Fp12 a, in Fp12 b) - { - var aa = a.C0 * b.C0; - var bb = a.C1 * b.C1; - var o = b.C0 + b.C1; - var c1 = a.C1 + a.C0; - c1 *= o; - c1 -= aa; - c1 -= bb; - var c0 = bb.MulByNonresidue(); - c0 += aa; - - return new Fp12(in c0, in c1); - } + return new Fp12(in c0, in c1); + } - #region Instance math methods + #region Instance math methods - public Fp12 Negate() => -this; - public Fp12 Multiply(in Fp12 value) => this * value; - public Fp12 Sum(in Fp12 value) => this + value; - public Fp12 Subtract(in Fp12 value) => this - value; + public Fp12 Negate() => -this; + public Fp12 Multiply(in Fp12 value) => this * value; + public Fp12 Sum(in Fp12 value) => this + value; + public Fp12 Subtract(in Fp12 value) => this - value; - #endregion + #endregion + } } diff --git a/src/Neo.Cryptography.BLS12_381/Fp2.cs b/src/Neo.Cryptography.BLS12_381/Fp2.cs index 4ca3830970..8b035b0660 100644 --- a/src/Neo.Cryptography.BLS12_381/Fp2.cs +++ b/src/Neo.Cryptography.BLS12_381/Fp2.cs @@ -14,261 +14,262 @@ using System.Security.Cryptography; using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Size)] -public readonly struct Fp2 : IEquatable, INumber +namespace Neo.Cryptography.BLS12_381 { - [FieldOffset(0)] - public readonly Fp C0; - [FieldOffset(Fp.Size)] - public readonly Fp C1; - - public const int Size = Fp.Size * 2; - - private static readonly Fp2 _zero = new(); - private static readonly Fp2 _one = new(in Fp.One); - - public static ref readonly Fp2 Zero => ref _zero; - public static ref readonly Fp2 One => ref _one; - - public bool IsZero => C0.IsZero & C1.IsZero; - - public Fp2(in Fp f) - : this(in f, in Fp.Zero) + [StructLayout(LayoutKind.Explicit, Size = Size)] + public readonly struct Fp2 : IEquatable, INumber { - } + [FieldOffset(0)] + public readonly Fp C0; + [FieldOffset(Fp.Size)] + public readonly Fp C1; - public Fp2(in Fp c0, in Fp c1) - { - C0 = c0; - C1 = c1; - } + public const int Size = Fp.Size * 2; - public static Fp2 FromBytes(ReadOnlySpan data) - { - if (data.Length != Size) - throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); - Fp c0 = Fp.FromBytes(data[Fp.Size..]); - Fp c1 = Fp.FromBytes(data[..Fp.Size]); - return new(in c0, in c1); - } + private static readonly Fp2 _zero = new(); + private static readonly Fp2 _one = new(in Fp.One); - public static Fp2 Random(RandomNumberGenerator rng) - { - return new(Fp.Random(rng), Fp.Random(rng)); - } + public static ref readonly Fp2 Zero => ref _zero; + public static ref readonly Fp2 One => ref _one; - public static bool operator ==(in Fp2 a, in Fp2 b) - { - return a.C0 == b.C0 & a.C1 == b.C1; - } + public bool IsZero => C0.IsZero & C1.IsZero; - public static bool operator !=(in Fp2 a, in Fp2 b) - { - return !(a == b); - } + public Fp2(in Fp f) + : this(in f, in Fp.Zero) + { + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not Fp2 other) return false; - return this == other; - } + public Fp2(in Fp c0, in Fp c1) + { + C0 = c0; + C1 = c1; + } - public bool Equals(Fp2 other) - { - return this == other; - } + public static Fp2 FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + Fp c0 = Fp.FromBytes(data[Fp.Size..]); + Fp c1 = Fp.FromBytes(data[..Fp.Size]); + return new(in c0, in c1); + } + + public static Fp2 Random(RandomNumberGenerator rng) + { + return new(Fp.Random(rng), Fp.Random(rng)); + } - public override int GetHashCode() - { - return C0.GetHashCode() ^ C1.GetHashCode(); - } + public static bool operator ==(in Fp2 a, in Fp2 b) + { + return a.C0 == b.C0 & a.C1 == b.C1; + } - public byte[] ToArray() - { - byte[] result = new byte[Size]; - TryWrite(result); - return result; - } + public static bool operator !=(in Fp2 a, in Fp2 b) + { + return !(a == b); + } - public bool TryWrite(Span buffer) - { - if (buffer.Length < Size) return false; - C0.TryWrite(buffer[Fp.Size..Size]); - C1.TryWrite(buffer[0..Fp.Size]); - return true; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp2 other) return false; + return this == other; + } - public Fp2 FrobeniusMap() - { - // This is always just a conjugation. If you're curious why, here's - // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ - return Conjugate(); - } + public bool Equals(Fp2 other) + { + return this == other; + } - public Fp2 Conjugate() - { - return new(in C0, -C1); - } + public override int GetHashCode() + { + return C0.GetHashCode() ^ C1.GetHashCode(); + } - public Fp2 MulByNonresidue() - { - // Multiply a + bu by u + 1, getting - // au + a + bu^2 + bu - // and because u^2 = -1, we get - // (a - b) + (a + b)u + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } - return new(C0 - C1, C0 + C1); - } + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + C0.TryWrite(buffer[Fp.Size..Size]); + C1.TryWrite(buffer[0..Fp.Size]); + return true; + } - public bool LexicographicallyLargest() - { - // If this element's c1 coefficient is lexicographically largest - // then it is lexicographically largest. Otherwise, in the event - // the c1 coefficient is zero and the c0 coefficient is - // lexicographically largest, then this element is lexicographically - // largest. + public Fp2 FrobeniusMap() + { + // This is always just a conjugation. If you're curious why, here's + // an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/ + return Conjugate(); + } - return C1.LexicographicallyLargest() | (C1.IsZero & C0.LexicographicallyLargest()); - } + public Fp2 Conjugate() + { + return new(in C0, -C1); + } - public Fp2 Square() - { - // Complex squaring: - // - // v0 = c0 * c1 - // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 - // c1' = 2 * v0 - // - // In BLS12-381's F_{p^2}, our \beta is -1 so we - // can modify this formula: - // - // c0' = (c0 + c1) * (c0 - c1) - // c1' = 2 * c0 * c1 - - var a = C0 + C1; - var b = C0 - C1; - var c = C0 + C0; - - return new(a * b, c * C1); - } + public Fp2 MulByNonresidue() + { + // Multiply a + bu by u + 1, getting + // au + a + bu^2 + bu + // and because u^2 = -1, we get + // (a - b) + (a + b)u - public static Fp2 operator *(in Fp2 a, in Fp2 b) - { - // F_{p^2} x F_{p^2} multiplication implemented with operand scanning (schoolbook) - // computes the result as: - // - // a·b = (a_0 b_0 + a_1 b_1 β) + (a_0 b_1 + a_1 b_0)i - // - // In BLS12-381's F_{p^2}, our β is -1, so the resulting F_{p^2} element is: - // - // c_0 = a_0 b_0 - a_1 b_1 - // c_1 = a_0 b_1 + a_1 b_0 - // - // Each of these is a "sum of products", which we can compute efficiently. - - return new( - Fp.SumOfProducts(stackalloc[] { a.C0, -a.C1 }, stackalloc[] { b.C0, b.C1 }), - Fp.SumOfProducts(stackalloc[] { a.C0, a.C1 }, stackalloc[] { b.C1, b.C0 }) - ); - } + return new(C0 - C1, C0 + C1); + } - public static Fp2 operator +(in Fp2 a, in Fp2 b) - { - return new(a.C0 + b.C0, a.C1 + b.C1); - } + public bool LexicographicallyLargest() + { + // If this element's c1 coefficient is lexicographically largest + // then it is lexicographically largest. Otherwise, in the event + // the c1 coefficient is zero and the c0 coefficient is + // lexicographically largest, then this element is lexicographically + // largest. - public static Fp2 operator -(in Fp2 a, in Fp2 b) - { - return new(a.C0 - b.C0, a.C1 - b.C1); - } + return C1.LexicographicallyLargest() | (C1.IsZero & C0.LexicographicallyLargest()); + } - public static Fp2 operator -(in Fp2 a) - { - return new(-a.C0, -a.C1); - } + public Fp2 Square() + { + // Complex squaring: + // + // v0 = c0 * c1 + // c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0 + // c1' = 2 * v0 + // + // In BLS12-381's F_{p^2}, our \beta is -1 so we + // can modify this formula: + // + // c0' = (c0 + c1) * (c0 - c1) + // c1' = 2 * c0 * c1 + + var a = C0 + C1; + var b = C0 - C1; + var c = C0 + C0; + + return new(a * b, c * C1); + } + + public static Fp2 operator *(in Fp2 a, in Fp2 b) + { + // F_{p^2} x F_{p^2} multiplication implemented with operand scanning (schoolbook) + // computes the result as: + // + // a·b = (a_0 b_0 + a_1 b_1 β) + (a_0 b_1 + a_1 b_0)i + // + // In BLS12-381's F_{p^2}, our β is -1, so the resulting F_{p^2} element is: + // + // c_0 = a_0 b_0 - a_1 b_1 + // c_1 = a_0 b_1 + a_1 b_0 + // + // Each of these is a "sum of products", which we can compute efficiently. + + return new( + Fp.SumOfProducts(stackalloc[] { a.C0, -a.C1 }, stackalloc[] { b.C0, b.C1 }), + Fp.SumOfProducts(stackalloc[] { a.C0, a.C1 }, stackalloc[] { b.C1, b.C0 }) + ); + } + + public static Fp2 operator +(in Fp2 a, in Fp2 b) + { + return new(a.C0 + b.C0, a.C1 + b.C1); + } - public Fp2 Sqrt() - { - // Algorithm 9, https://eprint.iacr.org/2012/685.pdf - // with constant time modifications. + public static Fp2 operator -(in Fp2 a, in Fp2 b) + { + return new(a.C0 - b.C0, a.C1 - b.C1); + } - // a1 = self^((p - 3) / 4) - var a1 = this.PowVartime(new ulong[] + public static Fp2 operator -(in Fp2 a) { - 0xee7f_bfff_ffff_eaaa, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6 - }); - - // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) - var alpha = a1.Square() * this; - - // x0 = self^((p + 1) / 4) - var x0 = a1 * this; - - // (1 + alpha)^((q - 1) // 2) * x0 - var sqrt = (alpha + One).PowVartime(new ulong[] { - 0xdcff_7fff_ffff_d555, - 0x0f55_ffff_58a9_ffff, - 0xb398_6950_7b58_7b12, - 0xb23b_a5c2_79c2_895f, - 0x258d_d3db_21a5_d66b, - 0x0d00_88f5_1cbf_f34d, - }) * x0; - - // In the event that alpha = -1, the element is order p - 1 and so - // we're just trying to get the square of an element of the subfield - // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element - // x0 = a + bu has b = 0, the solution is therefore au. - sqrt = ConditionalSelect(in sqrt, new(-x0.C1, in x0.C0), alpha == -One); - - sqrt = ConditionalSelect(in sqrt, in Zero, IsZero); - - // Only return the result if it's really the square root (and so - // self is actually quadratic nonresidue) - if (sqrt.Square() != this) throw new ArithmeticException(); - return sqrt; - } + return new(-a.C0, -a.C1); + } - public Fp2 Invert() - { - if (!TryInvert(out Fp2 result)) - throw new DivideByZeroException(); - return result; - } + public Fp2 Sqrt() + { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + // with constant time modifications. + + // a1 = self^((p - 3) / 4) + var a1 = this.PowVartime(new ulong[] + { + 0xee7f_bfff_ffff_eaaa, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6 + }); + + // alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2) + var alpha = a1.Square() * this; + + // x0 = self^((p + 1) / 4) + var x0 = a1 * this; + + // (1 + alpha)^((q - 1) // 2) * x0 + var sqrt = (alpha + One).PowVartime(new ulong[] { + 0xdcff_7fff_ffff_d555, + 0x0f55_ffff_58a9_ffff, + 0xb398_6950_7b58_7b12, + 0xb23b_a5c2_79c2_895f, + 0x258d_d3db_21a5_d66b, + 0x0d00_88f5_1cbf_f34d, + }) * x0; + + // In the event that alpha = -1, the element is order p - 1 and so + // we're just trying to get the square of an element of the subfield + // Fp. This is given by x0 * u, since u = sqrt(-1). Since the element + // x0 = a + bu has b = 0, the solution is therefore au. + sqrt = ConditionalSelect(in sqrt, new(-x0.C1, in x0.C0), alpha == -One); + + sqrt = ConditionalSelect(in sqrt, in Zero, IsZero); + + // Only return the result if it's really the square root (and so + // self is actually quadratic nonresidue) + if (sqrt.Square() != this) throw new ArithmeticException(); + return sqrt; + } + + public Fp2 Invert() + { + if (!TryInvert(out Fp2 result)) + throw new DivideByZeroException(); + return result; + } - public bool TryInvert(out Fp2 result) - { - // We wish to find the multiplicative inverse of a nonzero - // element a + bu in Fp2. We leverage an identity - // - // (a + bu)(a - bu) = a^2 + b^2 - // - // which holds because u^2 = -1. This can be rewritten as - // - // (a + bu)(a - bu)/(a^2 + b^2) = 1 - // - // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). - // This gives that (a - bu)/(a^2 + b^2) is the inverse - // of (a + bu). Importantly, this can be computing using - // only a single inversion in Fp. - - bool s = (C0.Square() + C1.Square()).TryInvert(out Fp t); - result = new Fp2(C0 * t, C1 * -t); - return s; + public bool TryInvert(out Fp2 result) + { + // We wish to find the multiplicative inverse of a nonzero + // element a + bu in Fp2. We leverage an identity + // + // (a + bu)(a - bu) = a^2 + b^2 + // + // which holds because u^2 = -1. This can be rewritten as + // + // (a + bu)(a - bu)/(a^2 + b^2) = 1 + // + // because a^2 + b^2 = 0 has no nonzero solutions for (a, b). + // This gives that (a - bu)/(a^2 + b^2) is the inverse + // of (a + bu). Importantly, this can be computing using + // only a single inversion in Fp. + + bool s = (C0.Square() + C1.Square()).TryInvert(out Fp t); + result = new Fp2(C0 * t, C1 * -t); + return s; + } + + #region Instance math methods + + public Fp2 Negate() => -this; + public Fp2 Multiply(in Fp2 value) => this * value; + public Fp2 Sum(in Fp2 value) => this + value; + public Fp2 Subtract(in Fp2 value) => this - value; + + #endregion } - - #region Instance math methods - - public Fp2 Negate() => -this; - public Fp2 Multiply(in Fp2 value) => this * value; - public Fp2 Sum(in Fp2 value) => this + value; - public Fp2 Subtract(in Fp2 value) => this - value; - - #endregion } diff --git a/src/Neo.Cryptography.BLS12_381/Fp6.cs b/src/Neo.Cryptography.BLS12_381/Fp6.cs index 540bfc8a36..09a2691633 100644 --- a/src/Neo.Cryptography.BLS12_381/Fp6.cs +++ b/src/Neo.Cryptography.BLS12_381/Fp6.cs @@ -13,296 +13,297 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Size)] -public readonly struct Fp6 : IEquatable, INumber +namespace Neo.Cryptography.BLS12_381 { - [FieldOffset(0)] - public readonly Fp2 C0; - [FieldOffset(Fp2.Size)] - public readonly Fp2 C1; - [FieldOffset(Fp2.Size * 2)] - public readonly Fp2 C2; - - public const int Size = Fp2.Size * 3; - - private static readonly Fp6 _zero = new(); - private static readonly Fp6 _one = new(in Fp2.One); - - public static ref readonly Fp6 Zero => ref _zero; - public static ref readonly Fp6 One => ref _one; - - public bool IsZero => C0.IsZero & C1.IsZero & C2.IsZero; - - public Fp6(in Fp f) - : this(new Fp2(in f), in Fp2.Zero, in Fp2.Zero) + [StructLayout(LayoutKind.Explicit, Size = Size)] + public readonly struct Fp6 : IEquatable, INumber { - } + [FieldOffset(0)] + public readonly Fp2 C0; + [FieldOffset(Fp2.Size)] + public readonly Fp2 C1; + [FieldOffset(Fp2.Size * 2)] + public readonly Fp2 C2; - public Fp6(in Fp2 f) - : this(in f, in Fp2.Zero, in Fp2.Zero) - { - } + public const int Size = Fp2.Size * 3; - public Fp6(in Fp2 c0, in Fp2 c1, in Fp2 c2) - { - C0 = c0; - C1 = c1; - C2 = c2; - } + private static readonly Fp6 _zero = new(); + private static readonly Fp6 _one = new(in Fp2.One); - public static Fp6 FromBytes(ReadOnlySpan data) - { - if (data.Length != Size) - throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); - Fp2 c0 = Fp2.FromBytes(data[(Fp2.Size * 2)..]); - Fp2 c1 = Fp2.FromBytes(data[Fp2.Size..(Fp2.Size * 2)]); - Fp2 c2 = Fp2.FromBytes(data[..Fp2.Size]); - return new(in c0, in c1, in c2); - } + public static ref readonly Fp6 Zero => ref _zero; + public static ref readonly Fp6 One => ref _one; - public static bool operator ==(in Fp6 a, in Fp6 b) - { - return a.C0 == b.C0 & a.C1 == b.C1 & a.C2 == b.C2; - } + public bool IsZero => C0.IsZero & C1.IsZero & C2.IsZero; - public static bool operator !=(in Fp6 a, in Fp6 b) - { - return !(a == b); - } + public Fp6(in Fp f) + : this(new Fp2(in f), in Fp2.Zero, in Fp2.Zero) + { + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not Fp6 other) return false; - return this == other; - } + public Fp6(in Fp2 f) + : this(in f, in Fp2.Zero, in Fp2.Zero) + { + } - public bool Equals(Fp6 other) - { - return this == other; - } + public Fp6(in Fp2 c0, in Fp2 c1, in Fp2 c2) + { + C0 = c0; + C1 = c1; + C2 = c2; + } - public override int GetHashCode() - { - return C0.GetHashCode() ^ C1.GetHashCode() ^ C2.GetHashCode(); - } + public static Fp6 FromBytes(ReadOnlySpan data) + { + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + Fp2 c0 = Fp2.FromBytes(data[(Fp2.Size * 2)..]); + Fp2 c1 = Fp2.FromBytes(data[Fp2.Size..(Fp2.Size * 2)]); + Fp2 c2 = Fp2.FromBytes(data[..Fp2.Size]); + return new(in c0, in c1, in c2); + } + + public static bool operator ==(in Fp6 a, in Fp6 b) + { + return a.C0 == b.C0 & a.C1 == b.C1 & a.C2 == b.C2; + } - public byte[] ToArray() - { - byte[] result = new byte[Size]; - TryWrite(result); - return result; - } + public static bool operator !=(in Fp6 a, in Fp6 b) + { + return !(a == b); + } - public bool TryWrite(Span buffer) - { - if (buffer.Length < Size) return false; - C0.TryWrite(buffer[(Fp2.Size * 2)..Size]); - C1.TryWrite(buffer[Fp2.Size..(Fp2.Size * 2)]); - C2.TryWrite(buffer[0..Fp2.Size]); - return true; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Fp6 other) return false; + return this == other; + } - public static Fp6 Random(RandomNumberGenerator rng) - { - return new(Fp2.Random(rng), Fp2.Random(rng), Fp2.Random(rng)); - } + public bool Equals(Fp6 other) + { + return this == other; + } - internal Fp6 MulBy_1(in Fp2 c1) - { - var b_b = C1 * c1; + public override int GetHashCode() + { + return C0.GetHashCode() ^ C1.GetHashCode() ^ C2.GetHashCode(); + } - var t1 = (C1 + C2) * c1 - b_b; - t1 = t1.MulByNonresidue(); + public byte[] ToArray() + { + byte[] result = new byte[Size]; + TryWrite(result); + return result; + } - var t2 = (C0 + C1) * c1 - b_b; + public bool TryWrite(Span buffer) + { + if (buffer.Length < Size) return false; + C0.TryWrite(buffer[(Fp2.Size * 2)..Size]); + C1.TryWrite(buffer[Fp2.Size..(Fp2.Size * 2)]); + C2.TryWrite(buffer[0..Fp2.Size]); + return true; + } + + public static Fp6 Random(RandomNumberGenerator rng) + { + return new(Fp2.Random(rng), Fp2.Random(rng), Fp2.Random(rng)); + } - return new Fp6(in t1, in t2, in b_b); - } + internal Fp6 MulBy_1(in Fp2 c1) + { + var b_b = C1 * c1; - internal Fp6 MulBy_01(in Fp2 c0, in Fp2 c1) - { - var a_a = C0 * c0; - var b_b = C1 * c1; + var t1 = (C1 + C2) * c1 - b_b; + t1 = t1.MulByNonresidue(); - var t1 = (C1 + C2) * c1 - b_b; - t1 = t1.MulByNonresidue() + a_a; + var t2 = (C0 + C1) * c1 - b_b; - var t2 = (c0 + c1) * (C0 + C1) - a_a - b_b; + return new Fp6(in t1, in t2, in b_b); + } - var t3 = (C0 + C2) * c0 - a_a + b_b; + internal Fp6 MulBy_01(in Fp2 c0, in Fp2 c1) + { + var a_a = C0 * c0; + var b_b = C1 * c1; - return new Fp6(in t1, in t2, in t3); - } + var t1 = (C1 + C2) * c1 - b_b; + t1 = t1.MulByNonresidue() + a_a; - public Fp6 MulByNonresidue() - { - // Given a + bv + cv^2, this produces - // av + bv^2 + cv^3 - // but because v^3 = u + 1, we have - // c(u + 1) + av + v^2 + var t2 = (c0 + c1) * (C0 + C1) - a_a - b_b; - return new Fp6(C2.MulByNonresidue(), in C0, in C1); - } + var t3 = (C0 + C2) * c0 - a_a + b_b; - public Fp6 FrobeniusMap() - { - var c0 = C0.FrobeniusMap(); - var c1 = C1.FrobeniusMap(); - var c2 = C2.FrobeniusMap(); + return new Fp6(in t1, in t2, in t3); + } - // c1 = c1 * (u + 1)^((p - 1) / 3) - c1 *= new Fp2(in Fp.Zero, Fp.FromRawUnchecked(new ulong[] - { - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741 - })); - - // c2 = c2 * (u + 1)^((2p - 2) / 3) - c2 *= new Fp2(Fp.FromRawUnchecked(new ulong[] + public Fp6 MulByNonresidue() { - 0x890d_c9e4_8675_45c3, - 0x2af3_2253_3285_a5d5, - 0x5088_0866_309b_7e2c, - 0xa20d_1b8c_7e88_1024, - 0x14e4_f04f_e2db_9068, - 0x14e5_6d3f_1564_853a - }), in Fp.Zero); - - return new Fp6(c0, c1, c2); - } + // Given a + bv + cv^2, this produces + // av + bv^2 + cv^3 + // but because v^3 = u + 1, we have + // c(u + 1) + av + v^2 - public Fp6 Square() - { - var s0 = C0.Square(); - var ab = C0 * C1; - var s1 = ab + ab; - var s2 = (C0 - C1 + C2).Square(); - var bc = C1 * C2; - var s3 = bc + bc; - var s4 = C2.Square(); - - return new Fp6( - s3.MulByNonresidue() + s0, - s4.MulByNonresidue() + s1, - s1 + s2 + s3 - s0 - s4 - ); - } + return new Fp6(C2.MulByNonresidue(), in C0, in C1); + } - public Fp6 Invert() - { - var c0 = (C1 * C2).MulByNonresidue(); - c0 = C0.Square() - c0; + public Fp6 FrobeniusMap() + { + var c0 = C0.FrobeniusMap(); + var c1 = C1.FrobeniusMap(); + var c2 = C2.FrobeniusMap(); + + // c1 = c1 * (u + 1)^((p - 1) / 3) + c1 *= new Fp2(in Fp.Zero, Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + })); + + // c2 = c2 * (u + 1)^((2p - 2) / 3) + c2 *= new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x890d_c9e4_8675_45c3, + 0x2af3_2253_3285_a5d5, + 0x5088_0866_309b_7e2c, + 0xa20d_1b8c_7e88_1024, + 0x14e4_f04f_e2db_9068, + 0x14e5_6d3f_1564_853a + }), in Fp.Zero); + + return new Fp6(c0, c1, c2); + } + + public Fp6 Square() + { + var s0 = C0.Square(); + var ab = C0 * C1; + var s1 = ab + ab; + var s2 = (C0 - C1 + C2).Square(); + var bc = C1 * C2; + var s3 = bc + bc; + var s4 = C2.Square(); + + return new Fp6( + s3.MulByNonresidue() + s0, + s4.MulByNonresidue() + s1, + s1 + s2 + s3 - s0 - s4 + ); + } + + public Fp6 Invert() + { + var c0 = (C1 * C2).MulByNonresidue(); + c0 = C0.Square() - c0; - var c1 = C2.Square().MulByNonresidue(); - c1 -= C0 * C1; + var c1 = C2.Square().MulByNonresidue(); + c1 -= C0 * C1; - var c2 = C1.Square(); - c2 -= C0 * C2; + var c2 = C1.Square(); + c2 -= C0 * C2; - var t = (C1 * c2 + C2 * c1).MulByNonresidue(); - t += C0 * c0; + var t = (C1 * c2 + C2 * c1).MulByNonresidue(); + t += C0 * c0; - t = t.Invert(); - return new Fp6(t * c0, t * c1, t * c2); - } + t = t.Invert(); + return new Fp6(t * c0, t * c1, t * c2); + } - public static Fp6 operator -(in Fp6 a) - { - return new Fp6(-a.C0, -a.C1, -a.C2); - } + public static Fp6 operator -(in Fp6 a) + { + return new Fp6(-a.C0, -a.C1, -a.C2); + } - public static Fp6 operator +(in Fp6 a, in Fp6 b) - { - return new Fp6(a.C0 + b.C0, a.C1 + b.C1, a.C2 + b.C2); - } + public static Fp6 operator +(in Fp6 a, in Fp6 b) + { + return new Fp6(a.C0 + b.C0, a.C1 + b.C1, a.C2 + b.C2); + } - public static Fp6 operator -(in Fp6 a, in Fp6 b) - { - return new Fp6(a.C0 - b.C0, a.C1 - b.C1, a.C2 - b.C2); - } + public static Fp6 operator -(in Fp6 a, in Fp6 b) + { + return new Fp6(a.C0 - b.C0, a.C1 - b.C1, a.C2 - b.C2); + } - public static Fp6 operator *(in Fp6 a, in Fp6 b) - { - // The intuition for this algorithm is that we can look at F_p^6 as a direct - // extension of F_p^2, and express the overall operations down to the base field - // F_p instead of only over F_p^2. This enables us to interleave multiplications - // and reductions, ensuring that we don't require double-width intermediate - // representations (with around twice as many limbs as F_p elements). - - // We want to express the multiplication c = a x b, where a = (a_0, a_1, a_2) is - // an element of F_p^6, and a_i = (a_i,0, a_i,1) is an element of F_p^2. The fully - // expanded multiplication is given by (2022-376 §5): - // - // c_0,0 = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1 - // - a_1,0 b_2,1 - a_1,1 b_2,0 - a_2,0 b_1,1 - a_2,1 b_1,0. - // = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 (b_2,0 - b_2,1) - a_1,1 (b_2,0 + b_2,1) - // + a_2,0 (b_1,0 - b_1,1) - a_2,1 (b_1,0 + b_1,1). - // - // c_0,1 = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0 b_2,1 + a_1,1 b_2,0 + a_2,0 b_1,1 + a_2,1 b_1,0 - // + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1. - // = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0(b_2,0 + b_2,1) + a_1,1(b_2,0 - b_2,1) - // + a_2,0(b_1,0 + b_1,1) + a_2,1(b_1,0 - b_1,1). - // - // c_1,0 = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0 b_2,0 - a_2,1 b_2,1 - // - a_2,0 b_2,1 - a_2,1 b_2,0. - // = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0(b_2,0 - b_2,1) - // - a_2,1(b_2,0 + b_2,1). - // - // c_1,1 = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0 b_2,1 + a_2,1 b_2,0 - // + a_2,0 b_2,0 - a_2,1 b_2,1 - // = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0(b_2,0 + b_2,1) - // + a_2,1(b_2,0 - b_2,1). - // - // c_2,0 = a_0,0 b_2,0 - a_0,1 b_2,1 + a_1,0 b_1,0 - a_1,1 b_1,1 + a_2,0 b_0,0 - a_2,1 b_0,1. - // c_2,1 = a_0,0 b_2,1 + a_0,1 b_2,0 + a_1,0 b_1,1 + a_1,1 b_1,0 + a_2,0 b_0,1 + a_2,1 b_0,0. - // - // Each of these is a "sum of products", which we can compute efficiently. - - var b10_p_b11 = b.C1.C0 + b.C1.C1; - var b10_m_b11 = b.C1.C0 - b.C1.C1; - var b20_p_b21 = b.C2.C0 + b.C2.C1; - var b20_m_b21 = b.C2.C0 - b.C2.C1; - - return new Fp6(new Fp2( - Fp.SumOfProducts( - stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, - stackalloc[] { b.C0.C0, b.C0.C1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11 } - ), - Fp.SumOfProducts( - stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, - stackalloc[] { b.C0.C1, b.C0.C0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11 } - )), new Fp2( - Fp.SumOfProducts( - stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, - stackalloc[] { b.C1.C0, b.C1.C1, b.C0.C0, b.C0.C1, b20_m_b21, b20_p_b21 } - ), - Fp.SumOfProducts( - stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, - stackalloc[] { b.C1.C1, b.C1.C0, b.C0.C1, b.C0.C0, b20_p_b21, b20_m_b21 } - )), new Fp2( - Fp.SumOfProducts( - stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, - stackalloc[] { b.C2.C0, b.C2.C1, b.C1.C0, b.C1.C1, b.C0.C0, b.C0.C1 } - ), - Fp.SumOfProducts( - stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, - stackalloc[] { b.C2.C1, b.C2.C0, b.C1.C1, b.C1.C0, b.C0.C1, b.C0.C0 } - )) - ); + public static Fp6 operator *(in Fp6 a, in Fp6 b) + { + // The intuition for this algorithm is that we can look at F_p^6 as a direct + // extension of F_p^2, and express the overall operations down to the base field + // F_p instead of only over F_p^2. This enables us to interleave multiplications + // and reductions, ensuring that we don't require double-width intermediate + // representations (with around twice as many limbs as F_p elements). + + // We want to express the multiplication c = a x b, where a = (a_0, a_1, a_2) is + // an element of F_p^6, and a_i = (a_i,0, a_i,1) is an element of F_p^2. The fully + // expanded multiplication is given by (2022-376 §5): + // + // c_0,0 = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1 + // - a_1,0 b_2,1 - a_1,1 b_2,0 - a_2,0 b_1,1 - a_2,1 b_1,0. + // = a_0,0 b_0,0 - a_0,1 b_0,1 + a_1,0 (b_2,0 - b_2,1) - a_1,1 (b_2,0 + b_2,1) + // + a_2,0 (b_1,0 - b_1,1) - a_2,1 (b_1,0 + b_1,1). + // + // c_0,1 = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0 b_2,1 + a_1,1 b_2,0 + a_2,0 b_1,1 + a_2,1 b_1,0 + // + a_1,0 b_2,0 - a_1,1 b_2,1 + a_2,0 b_1,0 - a_2,1 b_1,1. + // = a_0,0 b_0,1 + a_0,1 b_0,0 + a_1,0(b_2,0 + b_2,1) + a_1,1(b_2,0 - b_2,1) + // + a_2,0(b_1,0 + b_1,1) + a_2,1(b_1,0 - b_1,1). + // + // c_1,0 = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0 b_2,0 - a_2,1 b_2,1 + // - a_2,0 b_2,1 - a_2,1 b_2,0. + // = a_0,0 b_1,0 - a_0,1 b_1,1 + a_1,0 b_0,0 - a_1,1 b_0,1 + a_2,0(b_2,0 - b_2,1) + // - a_2,1(b_2,0 + b_2,1). + // + // c_1,1 = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0 b_2,1 + a_2,1 b_2,0 + // + a_2,0 b_2,0 - a_2,1 b_2,1 + // = a_0,0 b_1,1 + a_0,1 b_1,0 + a_1,0 b_0,1 + a_1,1 b_0,0 + a_2,0(b_2,0 + b_2,1) + // + a_2,1(b_2,0 - b_2,1). + // + // c_2,0 = a_0,0 b_2,0 - a_0,1 b_2,1 + a_1,0 b_1,0 - a_1,1 b_1,1 + a_2,0 b_0,0 - a_2,1 b_0,1. + // c_2,1 = a_0,0 b_2,1 + a_0,1 b_2,0 + a_1,0 b_1,1 + a_1,1 b_1,0 + a_2,0 b_0,1 + a_2,1 b_0,0. + // + // Each of these is a "sum of products", which we can compute efficiently. + + var b10_p_b11 = b.C1.C0 + b.C1.C1; + var b10_m_b11 = b.C1.C0 - b.C1.C1; + var b20_p_b21 = b.C2.C0 + b.C2.C1; + var b20_m_b21 = b.C2.C0 - b.C2.C1; + + return new Fp6(new Fp2( + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, + stackalloc[] { b.C0.C0, b.C0.C1, b20_m_b21, b20_p_b21, b10_m_b11, b10_p_b11 } + ), + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, + stackalloc[] { b.C0.C1, b.C0.C0, b20_p_b21, b20_m_b21, b10_p_b11, b10_m_b11 } + )), new Fp2( + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, + stackalloc[] { b.C1.C0, b.C1.C1, b.C0.C0, b.C0.C1, b20_m_b21, b20_p_b21 } + ), + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, + stackalloc[] { b.C1.C1, b.C1.C0, b.C0.C1, b.C0.C0, b20_p_b21, b20_m_b21 } + )), new Fp2( + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, -a.C0.C1, a.C1.C0, -a.C1.C1, a.C2.C0, -a.C2.C1 }, + stackalloc[] { b.C2.C0, b.C2.C1, b.C1.C0, b.C1.C1, b.C0.C0, b.C0.C1 } + ), + Fp.SumOfProducts( + stackalloc[] { a.C0.C0, a.C0.C1, a.C1.C0, a.C1.C1, a.C2.C0, a.C2.C1 }, + stackalloc[] { b.C2.C1, b.C2.C0, b.C1.C1, b.C1.C0, b.C0.C1, b.C0.C0 } + )) + ); + } + + #region Instance math methods + + public Fp6 Negate() => -this; + public Fp6 Multiply(in Fp6 value) => this * value; + public Fp6 Sum(in Fp6 value) => this + value; + public Fp6 Subtract(in Fp6 value) => this - value; + + #endregion } - - #region Instance math methods - - public Fp6 Negate() => -this; - public Fp6 Multiply(in Fp6 value) => this * value; - public Fp6 Sum(in Fp6 value) => this + value; - public Fp6 Subtract(in Fp6 value) => this - value; - - #endregion } diff --git a/src/Neo.Cryptography.BLS12_381/FpConstants.cs b/src/Neo.Cryptography.BLS12_381/FpConstants.cs index dd9f7ece9f..cb02a3c9c6 100644 --- a/src/Neo.Cryptography.BLS12_381/FpConstants.cs +++ b/src/Neo.Cryptography.BLS12_381/FpConstants.cs @@ -9,76 +9,77 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class FpConstants +namespace Neo.Cryptography.BLS12_381 { - // p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 - public static readonly ulong[] MODULUS = + static class FpConstants { - 0xb9fe_ffff_ffff_aaab, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a - }; + // p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 + public static readonly ulong[] MODULUS = + { + 0xb9fe_ffff_ffff_aaab, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a + }; - // p - 2 - public static readonly ulong[] P_2 = - { - 0xb9fe_ffff_ffff_aaa9, - 0x1eab_fffe_b153_ffff, - 0x6730_d2a0_f6b0_f624, - 0x6477_4b84_f385_12bf, - 0x4b1b_a7b6_434b_acd7, - 0x1a01_11ea_397f_e69a - }; + // p - 2 + public static readonly ulong[] P_2 = + { + 0xb9fe_ffff_ffff_aaa9, + 0x1eab_fffe_b153_ffff, + 0x6730_d2a0_f6b0_f624, + 0x6477_4b84_f385_12bf, + 0x4b1b_a7b6_434b_acd7, + 0x1a01_11ea_397f_e69a + }; - // (p + 1) / 4 - public static readonly ulong[] P_1_4 = - { - 0xee7f_bfff_ffff_eaab, - 0x07aa_ffff_ac54_ffff, - 0xd9cc_34a8_3dac_3d89, - 0xd91d_d2e1_3ce1_44af, - 0x92c6_e9ed_90d2_eb35, - 0x0680_447a_8e5f_f9a6 - }; + // (p + 1) / 4 + public static readonly ulong[] P_1_4 = + { + 0xee7f_bfff_ffff_eaab, + 0x07aa_ffff_ac54_ffff, + 0xd9cc_34a8_3dac_3d89, + 0xd91d_d2e1_3ce1_44af, + 0x92c6_e9ed_90d2_eb35, + 0x0680_447a_8e5f_f9a6 + }; - // INV = -(p^{-1} mod 2^64) mod 2^64 - public const ulong INV = 0x89f3_fffc_fffc_fffd; + // INV = -(p^{-1} mod 2^64) mod 2^64 + public const ulong INV = 0x89f3_fffc_fffc_fffd; - // R = 2^384 mod p - public static readonly Fp R = Fp.FromRawUnchecked(new ulong[] - { - 0x7609_0000_0002_fffd, - 0xebf4_000b_c40c_0002, - 0x5f48_9857_53c7_58ba, - 0x77ce_5853_7052_5745, - 0x5c07_1a97_a256_ec6d, - 0x15f6_5ec3_fa80_e493 - }); + // R = 2^384 mod p + public static readonly Fp R = Fp.FromRawUnchecked(new ulong[] + { + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493 + }); - // R2 = 2^(384*2) mod p - public static readonly Fp R2 = Fp.FromRawUnchecked(new ulong[] - { - 0xf4df_1f34_1c34_1746, - 0x0a76_e6a6_09d1_04f1, - 0x8de5_476c_4c95_b6d5, - 0x67eb_88a9_939d_83c0, - 0x9a79_3e85_b519_952d, - 0x1198_8fe5_92ca_e3aa - }); + // R2 = 2^(384*2) mod p + public static readonly Fp R2 = Fp.FromRawUnchecked(new ulong[] + { + 0xf4df_1f34_1c34_1746, + 0x0a76_e6a6_09d1_04f1, + 0x8de5_476c_4c95_b6d5, + 0x67eb_88a9_939d_83c0, + 0x9a79_3e85_b519_952d, + 0x1198_8fe5_92ca_e3aa + }); - // R3 = 2^(384*3) mod p - public static readonly Fp R3 = Fp.FromRawUnchecked(new ulong[] - { - 0xed48_ac6b_d94c_a1e0, - 0x315f_831e_03a7_adf8, - 0x9a53_352a_615e_29dd, - 0x34c0_4e5e_921e_1761, - 0x2512_d435_6572_4728, - 0x0aa6_3460_9175_5d4d - }); + // R3 = 2^(384*3) mod p + public static readonly Fp R3 = Fp.FromRawUnchecked(new ulong[] + { + 0xed48_ac6b_d94c_a1e0, + 0x315f_831e_03a7_adf8, + 0x9a53_352a_615e_29dd, + 0x34c0_4e5e_921e_1761, + 0x2512_d435_6572_4728, + 0x0aa6_3460_9175_5d4d + }); + } } diff --git a/src/Neo.Cryptography.BLS12_381/G1Affine.cs b/src/Neo.Cryptography.BLS12_381/G1Affine.cs index cbed66bc8f..90b00c7fc3 100644 --- a/src/Neo.Cryptography.BLS12_381/G1Affine.cs +++ b/src/Neo.Cryptography.BLS12_381/G1Affine.cs @@ -13,169 +13,170 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.G1Constants; -namespace Neo.Cryptography.BLS12_381; - -public readonly struct G1Affine : IEquatable +namespace Neo.Cryptography.BLS12_381 { - public readonly Fp X; - public readonly Fp Y; - public readonly bool Infinity; + public readonly struct G1Affine : IEquatable + { + public readonly Fp X; + public readonly Fp Y; + public readonly bool Infinity; - public static readonly G1Affine Identity = new(in Fp.Zero, in Fp.One, true); - public static readonly G1Affine Generator = new(in GeneratorX, in GeneratorY, false); + public static readonly G1Affine Identity = new(in Fp.Zero, in Fp.One, true); + public static readonly G1Affine Generator = new(in GeneratorX, in GeneratorY, false); - public bool IsIdentity => Infinity; - public bool IsTorsionFree => -new G1Projective(this).MulByX().MulByX() == new G1Projective(Endomorphism()); - public bool IsOnCurve => ((Y.Square() - (X.Square() * X)) == B) | Infinity; + public bool IsIdentity => Infinity; + public bool IsTorsionFree => -new G1Projective(this).MulByX().MulByX() == new G1Projective(Endomorphism()); + public bool IsOnCurve => ((Y.Square() - (X.Square() * X)) == B) | Infinity; - public G1Affine(in Fp x, in Fp y) - : this(in x, in y, false) - { - } + public G1Affine(in Fp x, in Fp y) + : this(in x, in y, false) + { + } - private G1Affine(in Fp x, in Fp y, bool infinity) - { - X = x; - Y = y; - Infinity = infinity; - } + private G1Affine(in Fp x, in Fp y, bool infinity) + { + X = x; + Y = y; + Infinity = infinity; + } - public G1Affine(in G1Projective p) - { - bool s = p.Z.TryInvert(out Fp zinv); + public G1Affine(in G1Projective p) + { + bool s = p.Z.TryInvert(out Fp zinv); - zinv = ConditionalSelect(in Fp.Zero, in zinv, s); - Fp x = p.X * zinv; - Fp y = p.Y * zinv; + zinv = ConditionalSelect(in Fp.Zero, in zinv, s); + Fp x = p.X * zinv; + Fp y = p.Y * zinv; - G1Affine tmp = new(in x, in y, false); - this = ConditionalSelect(in tmp, in Identity, !s); - } + G1Affine tmp = new(in x, in y, false); + this = ConditionalSelect(in tmp, in Identity, !s); + } - public static G1Affine FromUncompressed(ReadOnlySpan data) - { - return FromBytes(data, false, true); - } + public static G1Affine FromUncompressed(ReadOnlySpan data) + { + return FromBytes(data, false, true); + } - public static G1Affine FromCompressed(ReadOnlySpan data) - { - return FromBytes(data, true, true); - } + public static G1Affine FromCompressed(ReadOnlySpan data) + { + return FromBytes(data, true, true); + } - private static G1Affine FromBytes(ReadOnlySpan data, bool compressed, bool check) - { - bool compression_flag_set = (data[0] & 0x80) != 0; - bool infinity_flag_set = (data[0] & 0x40) != 0; - bool sort_flag_set = (data[0] & 0x20) != 0; - byte[] tmp = data[0..48].ToArray(); - tmp[0] &= 0b0001_1111; - Fp x = Fp.FromBytes(tmp); - if (compressed) - { - Fp y = ((x.Square() * x) + B).Sqrt(); - y = ConditionalSelect(in y, -y, y.LexicographicallyLargest() ^ sort_flag_set); - G1Affine result = new(in x, in y, infinity_flag_set); - result = ConditionalSelect(in result, in Identity, infinity_flag_set); - if (check) + private static G1Affine FromBytes(ReadOnlySpan data, bool compressed, bool check) + { + bool compression_flag_set = (data[0] & 0x80) != 0; + bool infinity_flag_set = (data[0] & 0x40) != 0; + bool sort_flag_set = (data[0] & 0x20) != 0; + byte[] tmp = data[0..48].ToArray(); + tmp[0] &= 0b0001_1111; + Fp x = Fp.FromBytes(tmp); + if (compressed) { - bool _checked = (!infinity_flag_set | (infinity_flag_set & !sort_flag_set & x.IsZero)) - & compression_flag_set; - _checked &= result.IsTorsionFree; - if (!_checked) throw new FormatException(); + Fp y = ((x.Square() * x) + B).Sqrt(); + y = ConditionalSelect(in y, -y, y.LexicographicallyLargest() ^ sort_flag_set); + G1Affine result = new(in x, in y, infinity_flag_set); + result = ConditionalSelect(in result, in Identity, infinity_flag_set); + if (check) + { + bool _checked = (!infinity_flag_set | (infinity_flag_set & !sort_flag_set & x.IsZero)) + & compression_flag_set; + _checked &= result.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + return result; } - return result; - } - else - { - Fp y = Fp.FromBytes(data[48..96]); - G1Affine result = ConditionalSelect(new(in x, in y, infinity_flag_set), in Identity, infinity_flag_set); - if (check) + else { - bool _checked = (!infinity_flag_set | (infinity_flag_set & x.IsZero & y.IsZero)) - & !compression_flag_set - & !sort_flag_set; - _checked &= result.IsOnCurve & result.IsTorsionFree; - if (!_checked) throw new FormatException(); + Fp y = Fp.FromBytes(data[48..96]); + G1Affine result = ConditionalSelect(new(in x, in y, infinity_flag_set), in Identity, infinity_flag_set); + if (check) + { + bool _checked = (!infinity_flag_set | (infinity_flag_set & x.IsZero & y.IsZero)) + & !compression_flag_set + & !sort_flag_set; + _checked &= result.IsOnCurve & result.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + return result; } - return result; } - } - public static bool operator ==(in G1Affine a, in G1Affine b) - { - return (a.Infinity & b.Infinity) | (!a.Infinity & !b.Infinity & a.X == b.X & a.Y == b.Y); - } + public static bool operator ==(in G1Affine a, in G1Affine b) + { + return (a.Infinity & b.Infinity) | (!a.Infinity & !b.Infinity & a.X == b.X & a.Y == b.Y); + } - public static bool operator !=(in G1Affine a, in G1Affine b) - { - return !(a == b); - } + public static bool operator !=(in G1Affine a, in G1Affine b) + { + return !(a == b); + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not G1Affine other) return false; - return this == other; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G1Affine other) return false; + return this == other; + } - public bool Equals(G1Affine other) - { - return this == other; - } + public bool Equals(G1Affine other) + { + return this == other; + } - public override int GetHashCode() - { - if (Infinity) return Infinity.GetHashCode(); - return X.GetHashCode() ^ Y.GetHashCode(); - } + public override int GetHashCode() + { + if (Infinity) return Infinity.GetHashCode(); + return X.GetHashCode() ^ Y.GetHashCode(); + } - public static G1Affine operator -(in G1Affine p) - { - return new G1Affine(in p.X, ConditionalSelect(-p.Y, in Fp.One, p.Infinity), p.Infinity); - } + public static G1Affine operator -(in G1Affine p) + { + return new G1Affine(in p.X, ConditionalSelect(-p.Y, in Fp.One, p.Infinity), p.Infinity); + } - public byte[] ToCompressed() - { - byte[] res = ConditionalSelect(in X, in Fp.Zero, Infinity).ToArray(); + public byte[] ToCompressed() + { + byte[] res = ConditionalSelect(in X, in Fp.Zero, Infinity).ToArray(); - // This point is in compressed form, so we set the most significant bit. - res[0] |= 0x80; + // This point is in compressed form, so we set the most significant bit. + res[0] |= 0x80; - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= ConditionalSelect((byte)0, (byte)0x20, !Infinity & Y.LexicographicallyLargest()); + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= ConditionalSelect((byte)0, (byte)0x20, !Infinity & Y.LexicographicallyLargest()); - return res; - } + return res; + } - public byte[] ToUncompressed() - { - byte[] res = new byte[96]; + public byte[] ToUncompressed() + { + byte[] res = new byte[96]; - ConditionalSelect(in X, in Fp.Zero, Infinity).TryWrite(res.AsSpan(0..48)); - ConditionalSelect(in Y, in Fp.Zero, Infinity).TryWrite(res.AsSpan(48..96)); + ConditionalSelect(in X, in Fp.Zero, Infinity).TryWrite(res.AsSpan(0..48)); + ConditionalSelect(in Y, in Fp.Zero, Infinity).TryWrite(res.AsSpan(48..96)); - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); - return res; - } + return res; + } - public G1Projective ToCurve() - { - return new(this); - } + public G1Projective ToCurve() + { + return new(this); + } - private G1Affine Endomorphism() - { - return new(X * BETA, in Y, Infinity); - } + private G1Affine Endomorphism() + { + return new(X * BETA, in Y, Infinity); + } - public static G1Projective operator *(in G1Affine a, in Scalar b) - { - return new G1Projective(in a) * b.ToArray(); + public static G1Projective operator *(in G1Affine a, in Scalar b) + { + return new G1Projective(in a) * b.ToArray(); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/G1Constants.cs b/src/Neo.Cryptography.BLS12_381/G1Constants.cs index 1c07bb0900..3fa5bdf6ae 100644 --- a/src/Neo.Cryptography.BLS12_381/G1Constants.cs +++ b/src/Neo.Cryptography.BLS12_381/G1Constants.cs @@ -9,47 +9,48 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class G1Constants +namespace Neo.Cryptography.BLS12_381 { - public static readonly Fp GeneratorX = Fp.FromRawUnchecked(new ulong[] + static class G1Constants { - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75 - }); + public static readonly Fp GeneratorX = Fp.FromRawUnchecked(new ulong[] + { + 0x5cb3_8790_fd53_0c16, + 0x7817_fc67_9976_fff5, + 0x154f_95c7_143b_a1c1, + 0xf0ae_6acd_f3d0_e747, + 0xedce_6ecc_21db_f440, + 0x1201_7741_9e0b_fb75 + }); - public static readonly Fp GeneratorY = Fp.FromRawUnchecked(new ulong[] - { - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a - }); + public static readonly Fp GeneratorY = Fp.FromRawUnchecked(new ulong[] + { + 0xbaac_93d5_0ce7_2271, + 0x8c22_631a_7918_fd8e, + 0xdd59_5f13_5707_25ce, + 0x51ac_5829_5040_5194, + 0x0e1c_8c3f_ad00_59c0, + 0x0bbc_3efc_5008_a26a + }); - public static readonly Fp B = Fp.FromRawUnchecked(new ulong[] - { - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e - }); + public static readonly Fp B = Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + }); - public static readonly Fp BETA = Fp.FromRawUnchecked(new ulong[] - { - 0x30f1_361b_798a_64e8, - 0xf3b8_ddab_7ece_5a2a, - 0x16a8_ca3a_c615_77f7, - 0xc26a_2ff8_74fd_029b, - 0x3636_b766_6070_1c6e, - 0x051b_a4ab_241b_6160 - }); + public static readonly Fp BETA = Fp.FromRawUnchecked(new ulong[] + { + 0x30f1_361b_798a_64e8, + 0xf3b8_ddab_7ece_5a2a, + 0x16a8_ca3a_c615_77f7, + 0xc26a_2ff8_74fd_029b, + 0x3636_b766_6070_1c6e, + 0x051b_a4ab_241b_6160 + }); + } } diff --git a/src/Neo.Cryptography.BLS12_381/G1Projective.cs b/src/Neo.Cryptography.BLS12_381/G1Projective.cs index 95600af135..f09411bb00 100644 --- a/src/Neo.Cryptography.BLS12_381/G1Projective.cs +++ b/src/Neo.Cryptography.BLS12_381/G1Projective.cs @@ -15,280 +15,281 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.G1Constants; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Fp.Size * 3)] -public readonly struct G1Projective : IEquatable +namespace Neo.Cryptography.BLS12_381 { - [FieldOffset(0)] - public readonly Fp X; - [FieldOffset(Fp.Size)] - public readonly Fp Y; - [FieldOffset(Fp.Size * 2)] - public readonly Fp Z; - - public static readonly G1Projective Identity = new(in Fp.Zero, in Fp.One, in Fp.Zero); - public static readonly G1Projective Generator = new(in GeneratorX, in GeneratorY, in Fp.One); - - public bool IsIdentity => Z.IsZero; - public bool IsOnCurve => ((Y.Square() * Z) == (X.Square() * X + Z.Square() * Z * B)) | Z.IsZero; - - public G1Projective(in Fp x, in Fp y, in Fp z) - { - X = x; - Y = y; - Z = z; - } - - public G1Projective(in G1Affine p) - : this(in p.X, in p.Y, ConditionalSelect(in Fp.One, in Fp.Zero, p.Infinity)) - { - } - - public static bool operator ==(in G1Projective a, in G1Projective b) + [StructLayout(LayoutKind.Explicit, Size = Fp.Size * 3)] + public readonly struct G1Projective : IEquatable { - // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? - - Fp x1 = a.X * b.Z; - Fp x2 = b.X * a.Z; + [FieldOffset(0)] + public readonly Fp X; + [FieldOffset(Fp.Size)] + public readonly Fp Y; + [FieldOffset(Fp.Size * 2)] + public readonly Fp Z; - Fp y1 = a.Y * b.Z; - Fp y2 = b.Y * a.Z; + public static readonly G1Projective Identity = new(in Fp.Zero, in Fp.One, in Fp.Zero); + public static readonly G1Projective Generator = new(in GeneratorX, in GeneratorY, in Fp.One); - bool self_is_zero = a.Z.IsZero; - bool other_is_zero = b.Z.IsZero; + public bool IsIdentity => Z.IsZero; + public bool IsOnCurve => ((Y.Square() * Z) == (X.Square() * X + Z.Square() * Z * B)) | Z.IsZero; - // Both point at infinity. Or neither point at infinity, coordinates are the same. - return (self_is_zero & other_is_zero) | ((!self_is_zero) & (!other_is_zero) & x1 == x2 & y1 == y2); - } + public G1Projective(in Fp x, in Fp y, in Fp z) + { + X = x; + Y = y; + Z = z; + } - public static bool operator !=(in G1Projective a, in G1Projective b) - { - return !(a == b); - } + public G1Projective(in G1Affine p) + : this(in p.X, in p.Y, ConditionalSelect(in Fp.One, in Fp.Zero, p.Infinity)) + { + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not G1Projective other) return false; - return this == other; - } + public static bool operator ==(in G1Projective a, in G1Projective b) + { + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? - public bool Equals(G1Projective other) - { - return this == other; - } + Fp x1 = a.X * b.Z; + Fp x2 = b.X * a.Z; - public override int GetHashCode() - { - return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); - } + Fp y1 = a.Y * b.Z; + Fp y2 = b.Y * a.Z; - public static G1Projective operator -(in G1Projective p) - { - return new G1Projective(in p.X, -p.Y, in p.Z); - } + bool self_is_zero = a.Z.IsZero; + bool other_is_zero = b.Z.IsZero; - private static Fp MulBy3B(in Fp a) - { - Fp b = a + a; - b += b; - return b + b + b; - } + // Both point at infinity. Or neither point at infinity, coordinates are the same. + return (self_is_zero & other_is_zero) | ((!self_is_zero) & (!other_is_zero) & x1 == x2 & y1 == y2); + } - public G1Projective Double() - { - Fp t0 = Y.Square(); - Fp z3 = t0 + t0; - z3 += z3; - z3 += z3; - Fp t1 = Y * Z; - Fp t2 = Z.Square(); - t2 = MulBy3B(in t2); - Fp x3 = t2 * z3; - Fp y3 = t0 + t2; - z3 = t1 * z3; - t1 = t2 + t2; - t2 = t1 + t2; - t0 -= t2; - y3 = t0 * y3; - y3 = x3 + y3; - t1 = X * Y; - x3 = t0 * t1; - x3 += x3; - - G1Projective tmp = new(in x3, in y3, in z3); - return ConditionalSelect(in tmp, in Identity, IsIdentity); - } + public static bool operator !=(in G1Projective a, in G1Projective b) + { + return !(a == b); + } - public static G1Projective operator +(in G1Projective a, in G1Projective b) - { - Fp t0 = a.X * b.X; - Fp t1 = a.Y * b.Y; - Fp t2 = a.Z * b.Z; - Fp t3 = a.X + a.Y; - Fp t4 = b.X + b.Y; - t3 *= t4; - t4 = t0 + t1; - t3 -= t4; - t4 = a.Y + a.Z; - Fp x3 = b.Y + b.Z; - t4 *= x3; - x3 = t1 + t2; - t4 -= x3; - x3 = a.X + a.Z; - Fp y3 = b.X + b.Z; - x3 *= y3; - y3 = t0 + t2; - y3 = x3 - y3; - x3 = t0 + t0; - t0 = x3 + t0; - t2 = MulBy3B(in t2); - Fp z3 = t1 + t2; - t1 -= t2; - y3 = MulBy3B(in y3); - x3 = t4 * y3; - t2 = t3 * t1; - x3 = t2 - x3; - y3 *= t0; - t1 *= z3; - y3 = t1 + y3; - t0 *= t3; - z3 *= t4; - z3 += t0; - - return new G1Projective(in x3, in y3, in z3); - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G1Projective other) return false; + return this == other; + } - public static G1Projective operator +(in G1Projective a, in G1Affine b) - { - Fp t0 = a.X * b.X; - Fp t1 = a.Y * b.Y; - Fp t3 = b.X + b.Y; - Fp t4 = a.X + a.Y; - t3 *= t4; - t4 = t0 + t1; - t3 -= t4; - t4 = b.Y * a.Z; - t4 += a.Y; - Fp y3 = b.X * a.Z; - y3 += a.X; - Fp x3 = t0 + t0; - t0 = x3 + t0; - Fp t2 = MulBy3B(in a.Z); - Fp z3 = t1 + t2; - t1 -= t2; - y3 = MulBy3B(in y3); - x3 = t4 * y3; - t2 = t3 * t1; - x3 = t2 - x3; - y3 *= t0; - t1 *= z3; - y3 = t1 + y3; - t0 *= t3; - z3 *= t4; - z3 += t0; - - G1Projective tmp = new(in x3, in y3, in z3); - return ConditionalSelect(in tmp, in a, b.IsIdentity); - } + public bool Equals(G1Projective other) + { + return this == other; + } - public static G1Projective operator +(in G1Affine a, in G1Projective b) - { - return b + a; - } + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); + } - public static G1Projective operator -(in G1Projective a, in G1Projective b) - { - return a + -b; - } + public static G1Projective operator -(in G1Projective p) + { + return new G1Projective(in p.X, -p.Y, in p.Z); + } - public static G1Projective operator -(in G1Projective a, in G1Affine b) - { - return a + -b; - } + private static Fp MulBy3B(in Fp a) + { + Fp b = a + a; + b += b; + return b + b + b; + } - public static G1Projective operator -(in G1Affine a, in G1Projective b) - { - return -b + a; - } + public G1Projective Double() + { + Fp t0 = Y.Square(); + Fp z3 = t0 + t0; + z3 += z3; + z3 += z3; + Fp t1 = Y * Z; + Fp t2 = Z.Square(); + t2 = MulBy3B(in t2); + Fp x3 = t2 * z3; + Fp y3 = t0 + t2; + z3 = t1 * z3; + t1 = t2 + t2; + t2 = t1 + t2; + t0 -= t2; + y3 = t0 * y3; + y3 = x3 + y3; + t1 = X * Y; + x3 = t0 * t1; + x3 += x3; + + G1Projective tmp = new(in x3, in y3, in z3); + return ConditionalSelect(in tmp, in Identity, IsIdentity); + } - public static G1Projective operator *(in G1Projective a, byte[] b) - { - int length = b.Length; - if (length != 32) - throw new ArgumentException($"The argument {nameof(b)} must be 32 bytes."); + public static G1Projective operator +(in G1Projective a, in G1Projective b) + { + Fp t0 = a.X * b.X; + Fp t1 = a.Y * b.Y; + Fp t2 = a.Z * b.Z; + Fp t3 = a.X + a.Y; + Fp t4 = b.X + b.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = a.Y + a.Z; + Fp x3 = b.Y + b.Z; + t4 *= x3; + x3 = t1 + t2; + t4 -= x3; + x3 = a.X + a.Z; + Fp y3 = b.X + b.Z; + x3 *= y3; + y3 = t0 + t2; + y3 = x3 - y3; + x3 = t0 + t0; + t0 = x3 + t0; + t2 = MulBy3B(in t2); + Fp z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(in y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + return new G1Projective(in x3, in y3, in z3); + } - G1Projective acc = Identity; + public static G1Projective operator +(in G1Projective a, in G1Affine b) + { + Fp t0 = a.X * b.X; + Fp t1 = a.Y * b.Y; + Fp t3 = b.X + b.Y; + Fp t4 = a.X + a.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = b.Y * a.Z; + t4 += a.Y; + Fp y3 = b.X * a.Z; + y3 += a.X; + Fp x3 = t0 + t0; + t0 = x3 + t0; + Fp t2 = MulBy3B(in a.Z); + Fp z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(in y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + G1Projective tmp = new(in x3, in y3, in z3); + return ConditionalSelect(in tmp, in a, b.IsIdentity); + } - foreach (bool bit in b - .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) - .Reverse() - .Skip(1)) + public static G1Projective operator +(in G1Affine a, in G1Projective b) { - acc = acc.Double(); - acc = ConditionalSelect(in acc, acc + a, bit); + return b + a; } - return acc; - } + public static G1Projective operator -(in G1Projective a, in G1Projective b) + { + return a + -b; + } - public static G1Projective operator *(in G1Projective a, in Scalar b) - { - return a * b.ToArray(); - } + public static G1Projective operator -(in G1Projective a, in G1Affine b) + { + return a + -b; + } - internal G1Projective MulByX() - { - G1Projective xself = Identity; + public static G1Projective operator -(in G1Affine a, in G1Projective b) + { + return -b + a; + } - ulong x = BLS_X >> 1; - G1Projective tmp = this; - while (x > 0) + public static G1Projective operator *(in G1Projective a, byte[] b) { - tmp = tmp.Double(); + int length = b.Length; + if (length != 32) + throw new ArgumentException($"The argument {nameof(b)} must be 32 bytes."); + + G1Projective acc = Identity; - if (x % 2 == 1) + foreach (bool bit in b + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse() + .Skip(1)) { - xself += tmp; + acc = acc.Double(); + acc = ConditionalSelect(in acc, acc + a, bit); } - x >>= 1; + + return acc; } - if (BLS_X_IS_NEGATIVE) + public static G1Projective operator *(in G1Projective a, in Scalar b) { - xself = -xself; + return a * b.ToArray(); } - return xself; - } - public G1Projective ClearCofactor() - { - return this - MulByX(); - } + internal G1Projective MulByX() + { + G1Projective xself = Identity; - public static void BatchNormalize(ReadOnlySpan p, Span q) - { - int length = p.Length; - if (length != q.Length) - throw new ArgumentException($"{nameof(p)} and {nameof(q)} must have the same length."); + ulong x = BLS_X >> 1; + G1Projective tmp = this; + while (x > 0) + { + tmp = tmp.Double(); - Span x = stackalloc Fp[length]; - Fp acc = Fp.One; - for (int i = 0; i < length; i++) - { - x[i] = acc; - acc = ConditionalSelect(acc * p[i].Z, in acc, p[i].IsIdentity); + if (x % 2 == 1) + { + xself += tmp; + } + x >>= 1; + } + + if (BLS_X_IS_NEGATIVE) + { + xself = -xself; + } + return xself; } - acc = acc.Invert(); + public G1Projective ClearCofactor() + { + return this - MulByX(); + } - for (int i = length - 1; i >= 0; i--) + public static void BatchNormalize(ReadOnlySpan p, Span q) { - bool skip = p[i].IsIdentity; - Fp tmp = x[i] * acc; - acc = ConditionalSelect(acc * p[i].Z, in acc, skip); - G1Affine qi = new(p[i].X * tmp, p[i].Y * tmp); - q[i] = ConditionalSelect(in qi, in G1Affine.Identity, skip); + int length = p.Length; + if (length != q.Length) + throw new ArgumentException($"{nameof(p)} and {nameof(q)} must have the same length."); + + Span x = stackalloc Fp[length]; + Fp acc = Fp.One; + for (int i = 0; i < length; i++) + { + x[i] = acc; + acc = ConditionalSelect(acc * p[i].Z, in acc, p[i].IsIdentity); + } + + acc = acc.Invert(); + + for (int i = length - 1; i >= 0; i--) + { + bool skip = p[i].IsIdentity; + Fp tmp = x[i] * acc; + acc = ConditionalSelect(acc * p[i].Z, in acc, skip); + G1Affine qi = new(p[i].X * tmp, p[i].Y * tmp); + q[i] = ConditionalSelect(in qi, in G1Affine.Identity, skip); + } } } } diff --git a/src/Neo.Cryptography.BLS12_381/G2Affine.cs b/src/Neo.Cryptography.BLS12_381/G2Affine.cs index 97256d02c7..a3468cb900 100644 --- a/src/Neo.Cryptography.BLS12_381/G2Affine.cs +++ b/src/Neo.Cryptography.BLS12_381/G2Affine.cs @@ -13,213 +13,214 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.G2Constants; -namespace Neo.Cryptography.BLS12_381; - -public readonly struct G2Affine : IEquatable +namespace Neo.Cryptography.BLS12_381 { - public readonly Fp2 X; - public readonly Fp2 Y; - public readonly bool Infinity; + public readonly struct G2Affine : IEquatable + { + public readonly Fp2 X; + public readonly Fp2 Y; + public readonly bool Infinity; - public static readonly G2Affine Identity = new(in Fp2.Zero, in Fp2.One, true); - public static readonly G2Affine Generator = new(in GeneratorX, in GeneratorY, false); + public static readonly G2Affine Identity = new(in Fp2.Zero, in Fp2.One, true); + public static readonly G2Affine Generator = new(in GeneratorX, in GeneratorY, false); - public bool IsIdentity => Infinity; - public bool IsTorsionFree - { - get + public bool IsIdentity => Infinity; + public bool IsTorsionFree { - // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130 - // Updated proof of correctness in https://eprint.iacr.org/2022/352 - // - // Check that psi(P) == [x] P - var p = new G2Projective(this); - return p.Psi() == p.MulByX(); + get + { + // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130 + // Updated proof of correctness in https://eprint.iacr.org/2022/352 + // + // Check that psi(P) == [x] P + var p = new G2Projective(this); + return p.Psi() == p.MulByX(); + } } - } - public bool IsOnCurve => ((Y.Square() - X.Square() * X) == B) | Infinity; // y^2 - x^3 ?= 4(u + 1) + public bool IsOnCurve => ((Y.Square() - X.Square() * X) == B) | Infinity; // y^2 - x^3 ?= 4(u + 1) - public G2Affine(in Fp2 x, in Fp2 y) - : this(in x, in y, false) - { - } + public G2Affine(in Fp2 x, in Fp2 y) + : this(in x, in y, false) + { + } - private G2Affine(in Fp2 x, in Fp2 y, bool infinity) - { - X = x; - Y = y; - Infinity = infinity; - } + private G2Affine(in Fp2 x, in Fp2 y, bool infinity) + { + X = x; + Y = y; + Infinity = infinity; + } - public G2Affine(in G2Projective p) - { - bool s = p.Z.TryInvert(out Fp2 zinv); + public G2Affine(in G2Projective p) + { + bool s = p.Z.TryInvert(out Fp2 zinv); - zinv = ConditionalSelect(in Fp2.Zero, in zinv, s); - Fp2 x = p.X * zinv; - Fp2 y = p.Y * zinv; + zinv = ConditionalSelect(in Fp2.Zero, in zinv, s); + Fp2 x = p.X * zinv; + Fp2 y = p.Y * zinv; - G2Affine tmp = new(in x, in y, false); - this = ConditionalSelect(in tmp, in Identity, !s); - } + G2Affine tmp = new(in x, in y, false); + this = ConditionalSelect(in tmp, in Identity, !s); + } - public static bool operator ==(in G2Affine a, in G2Affine b) - { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal + public static bool operator ==(in G2Affine a, in G2Affine b) + { + // The only cases in which two points are equal are + // 1. infinity is set on both + // 2. infinity is not set on both, and their coordinates are equal - return (a.Infinity & b.Infinity) | (!a.Infinity & !b.Infinity & a.X == b.X & a.Y == b.Y); - } + return (a.Infinity & b.Infinity) | (!a.Infinity & !b.Infinity & a.X == b.X & a.Y == b.Y); + } - public static bool operator !=(in G2Affine a, in G2Affine b) - { - return !(a == b); - } + public static bool operator !=(in G2Affine a, in G2Affine b) + { + return !(a == b); + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not G2Affine other) return false; - return this == other; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G2Affine other) return false; + return this == other; + } - public bool Equals(G2Affine other) - { - return this == other; - } + public bool Equals(G2Affine other) + { + return this == other; + } - public override int GetHashCode() - { - if (Infinity) return Infinity.GetHashCode(); - return X.GetHashCode() ^ Y.GetHashCode(); - } + public override int GetHashCode() + { + if (Infinity) return Infinity.GetHashCode(); + return X.GetHashCode() ^ Y.GetHashCode(); + } - public static G2Affine operator -(in G2Affine a) - { - return new G2Affine( - in a.X, - ConditionalSelect(-a.Y, in Fp2.One, a.Infinity), - a.Infinity - ); - } + public static G2Affine operator -(in G2Affine a) + { + return new G2Affine( + in a.X, + ConditionalSelect(-a.Y, in Fp2.One, a.Infinity), + a.Infinity + ); + } - public static G2Projective operator *(in G2Affine a, in Scalar b) - { - return new G2Projective(a) * b.ToArray(); - } + public static G2Projective operator *(in G2Affine a, in Scalar b) + { + return new G2Projective(a) * b.ToArray(); + } - public byte[] ToCompressed() - { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - var x = ConditionalSelect(in X, in Fp2.Zero, Infinity); + public byte[] ToCompressed() + { + // Strictly speaking, self.x is zero already when self.infinity is true, but + // to guard against implementation mistakes we do not assume this. + var x = ConditionalSelect(in X, in Fp2.Zero, Infinity); - var res = new byte[96]; + var res = new byte[96]; - x.C1.TryWrite(res.AsSpan(0..48)); - x.C0.TryWrite(res.AsSpan(48..96)); + x.C1.TryWrite(res.AsSpan(0..48)); + x.C0.TryWrite(res.AsSpan(48..96)); - // This point is in compressed form, so we set the most significant bit. - res[0] |= 0x80; + // This point is in compressed form, so we set the most significant bit. + res[0] |= 0x80; - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= ConditionalSelect((byte)0, (byte)0x20, !Infinity & Y.LexicographicallyLargest()); + // Is the y-coordinate the lexicographically largest of the two associated with the + // x-coordinate? If so, set the third-most significant bit so long as this is not + // the point at infinity. + res[0] |= ConditionalSelect((byte)0, (byte)0x20, !Infinity & Y.LexicographicallyLargest()); - return res; - } + return res; + } - public byte[] ToUncompressed() - { - var res = new byte[192]; + public byte[] ToUncompressed() + { + var res = new byte[192]; - var x = ConditionalSelect(in X, in Fp2.Zero, Infinity); - var y = ConditionalSelect(in Y, in Fp2.Zero, Infinity); + var x = ConditionalSelect(in X, in Fp2.Zero, Infinity); + var y = ConditionalSelect(in Y, in Fp2.Zero, Infinity); - x.C1.TryWrite(res.AsSpan(0..48)); - x.C0.TryWrite(res.AsSpan(48..96)); - y.C1.TryWrite(res.AsSpan(96..144)); - y.C0.TryWrite(res.AsSpan(144..192)); + x.C1.TryWrite(res.AsSpan(0..48)); + x.C0.TryWrite(res.AsSpan(48..96)); + y.C1.TryWrite(res.AsSpan(96..144)); + y.C0.TryWrite(res.AsSpan(144..192)); - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); + // Is this point at infinity? If so, set the second-most significant bit. + res[0] |= ConditionalSelect((byte)0, (byte)0x40, Infinity); - return res; - } + return res; + } - public static G2Affine FromUncompressed(ReadOnlySpan bytes) - { - return FromBytes(bytes, false, true); - } + public static G2Affine FromUncompressed(ReadOnlySpan bytes) + { + return FromBytes(bytes, false, true); + } - public static G2Affine FromCompressed(ReadOnlySpan bytes) - { - return FromBytes(bytes, true, true); - } + public static G2Affine FromCompressed(ReadOnlySpan bytes) + { + return FromBytes(bytes, true, true); + } - private static G2Affine FromBytes(ReadOnlySpan bytes, bool compressed, bool check) - { - // Obtain the three flags from the start of the byte sequence - bool compression_flag_set = (bytes[0] & 0x80) != 0; - bool infinity_flag_set = (bytes[0] & 0x40) != 0; - bool sort_flag_set = (bytes[0] & 0x20) != 0; - - // Attempt to obtain the x-coordinate - var tmp = bytes[0..48].ToArray(); - tmp[0] &= 0b0001_1111; - var xc1 = Fp.FromBytes(tmp); - var xc0 = Fp.FromBytes(bytes[48..96]); - var x = new Fp2(in xc0, in xc1); - - if (compressed) - { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - var y = ((x.Square() * x) + B).Sqrt(); - y = ConditionalSelect(in y, -y, y.LexicographicallyLargest() ^ sort_flag_set); - G2Affine result = new(in x, in y, infinity_flag_set); - result = ConditionalSelect(in result, in Identity, infinity_flag_set); - if (check) + private static G2Affine FromBytes(ReadOnlySpan bytes, bool compressed, bool check) + { + // Obtain the three flags from the start of the byte sequence + bool compression_flag_set = (bytes[0] & 0x80) != 0; + bool infinity_flag_set = (bytes[0] & 0x40) != 0; + bool sort_flag_set = (bytes[0] & 0x20) != 0; + + // Attempt to obtain the x-coordinate + var tmp = bytes[0..48].ToArray(); + tmp[0] &= 0b0001_1111; + var xc1 = Fp.FromBytes(tmp); + var xc0 = Fp.FromBytes(bytes[48..96]); + var x = new Fp2(in xc0, in xc1); + + if (compressed) { - bool _checked = (!infinity_flag_set | (infinity_flag_set & !sort_flag_set & x.IsZero)) - & compression_flag_set; - _checked &= result.IsTorsionFree; - if (!_checked) throw new FormatException(); + // Recover a y-coordinate given x by y = sqrt(x^3 + 4) + var y = ((x.Square() * x) + B).Sqrt(); + y = ConditionalSelect(in y, -y, y.LexicographicallyLargest() ^ sort_flag_set); + G2Affine result = new(in x, in y, infinity_flag_set); + result = ConditionalSelect(in result, in Identity, infinity_flag_set); + if (check) + { + bool _checked = (!infinity_flag_set | (infinity_flag_set & !sort_flag_set & x.IsZero)) + & compression_flag_set; + _checked &= result.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + return result; } - return result; - } - else - { - // Attempt to obtain the y-coordinate - var yc1 = Fp.FromBytes(bytes[96..144]); - var yc0 = Fp.FromBytes(bytes[144..192]); - var y = new Fp2(in yc0, in yc1); - - // Create a point representing this value - var p = ConditionalSelect(new G2Affine(in x, in y, infinity_flag_set), in Identity, infinity_flag_set); - - if (check) + else { - bool _checked = - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.IsZero & y.IsZero)) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set); - _checked &= p.IsOnCurve & p.IsTorsionFree; - if (!_checked) throw new FormatException(); + // Attempt to obtain the y-coordinate + var yc1 = Fp.FromBytes(bytes[96..144]); + var yc0 = Fp.FromBytes(bytes[144..192]); + var y = new Fp2(in yc0, in yc1); + + // Create a point representing this value + var p = ConditionalSelect(new G2Affine(in x, in y, infinity_flag_set), in Identity, infinity_flag_set); + + if (check) + { + bool _checked = + // If the infinity flag is set, the x and y coordinates should have been zero. + ((!infinity_flag_set) | (infinity_flag_set & x.IsZero & y.IsZero)) & + // The compression flag should not have been set, as this is an uncompressed element + (!compression_flag_set) & + // The sort flag should not have been set, as this is an uncompressed element + (!sort_flag_set); + _checked &= p.IsOnCurve & p.IsTorsionFree; + if (!_checked) throw new FormatException(); + } + + return p; } - - return p; } - } - public G2Projective ToCurve() - { - return new(this); + public G2Projective ToCurve() + { + return new(this); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/G2Constants.cs b/src/Neo.Cryptography.BLS12_381/G2Constants.cs index 045ad43a93..c6d4243b1b 100644 --- a/src/Neo.Cryptography.BLS12_381/G2Constants.cs +++ b/src/Neo.Cryptography.BLS12_381/G2Constants.cs @@ -9,104 +9,105 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class G2Constants +namespace Neo.Cryptography.BLS12_381 { - public static readonly Fp2 GeneratorX = new(Fp.FromRawUnchecked(new ulong[] - { - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7 - }), Fp.FromRawUnchecked(new ulong[] + static class G2Constants { - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3 - })); + public static readonly Fp2 GeneratorX = new(Fp.FromRawUnchecked(new ulong[] + { + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3 + })); - public static readonly Fp2 GeneratorY = new(Fp.FromRawUnchecked(new ulong[] - { - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2 - })); + public static readonly Fp2 GeneratorY = new(Fp.FromRawUnchecked(new ulong[] + { + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2 + })); - public static readonly Fp2 B = new(Fp.FromRawUnchecked(new ulong[] - { - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e - }), Fp.FromRawUnchecked(new ulong[] - { - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e - })); + public static readonly Fp2 B = new(Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + }), Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + })); - public static readonly Fp2 B3 = B + B + B; + public static readonly Fp2 B3 = B + B + B; - // 1 / ((u+1) ^ ((q-1)/3)) - public static readonly Fp2 PsiCoeffX = new(in Fp.Zero, Fp.FromRawUnchecked(new ulong[] - { - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a - })); + // 1 / ((u+1) ^ ((q-1)/3)) + public static readonly Fp2 PsiCoeffX = new(in Fp.Zero, Fp.FromRawUnchecked(new ulong[] + { + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a + })); - // 1 / ((u+1) ^ (p-1)/2) - public static readonly Fp2 PsiCoeffY = new(Fp.FromRawUnchecked(new ulong[] - { - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0x0bd592fc7d825ec8 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0x0e2b7eedbbfd87d2 - })); + // 1 / ((u+1) ^ (p-1)/2) + public static readonly Fp2 PsiCoeffY = new(Fp.FromRawUnchecked(new ulong[] + { + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2 + })); - // 1 / 2 ^ ((q-1)/3) - public static readonly Fp2 Psi2CoeffX = new(Fp.FromRawUnchecked(new ulong[] - { - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x03f97d6e83d050d2, - 0x18f0206554638741 - }), in Fp.Zero); + // 1 / 2 ^ ((q-1)/3) + public static readonly Fp2 Psi2CoeffX = new(Fp.FromRawUnchecked(new ulong[] + { + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741 + }), in Fp.Zero); + } } diff --git a/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs b/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs index c28b8889cb..4b0521f731 100644 --- a/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs +++ b/src/Neo.Cryptography.BLS12_381/G2Prepared.Adder.cs @@ -12,63 +12,64 @@ using System.Runtime.CompilerServices; using static Neo.Cryptography.BLS12_381.MillerLoopUtility; -namespace Neo.Cryptography.BLS12_381; - -partial class G2Prepared +namespace Neo.Cryptography.BLS12_381 { - class Adder : IMillerLoopDriver + partial class G2Prepared { - public G2Projective Curve; - public readonly G2Affine Base; - public readonly List<(Fp2, Fp2, Fp2)> Coeffs; - - public Adder(in G2Affine q) + class Adder : IMillerLoopDriver { - Curve = new G2Projective(in q); - Base = q; - Coeffs = new(68); - } + public G2Projective Curve; + public readonly G2Affine Base; + public readonly List<(Fp2, Fp2, Fp2)> Coeffs; - object? IMillerLoopDriver.DoublingStep(in object? f) - { - var coeffs = DoublingStep(ref Curve); - Coeffs.Add(coeffs); - return null; - } + public Adder(in G2Affine q) + { + Curve = new G2Projective(in q); + Base = q; + Coeffs = new(68); + } - object? IMillerLoopDriver.AdditionStep(in object? f) - { - var coeffs = AdditionStep(ref Curve, in Base); - Coeffs.Add(coeffs); - return null; - } + object? IMillerLoopDriver.DoublingStep(in object? f) + { + var coeffs = DoublingStep(ref Curve); + Coeffs.Add(coeffs); + return null; + } - #region IMillerLoopDriver + object? IMillerLoopDriver.AdditionStep(in object? f) + { + var coeffs = AdditionStep(ref Curve, in Base); + Coeffs.Add(coeffs); + return null; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object? Square(in object? f) => null; + #region IMillerLoopDriver - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object? Conjugate(in object? f) => null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object? Square(in object? f) => null; - public static object? One - { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => null; - } + public static object? Conjugate(in object? f) => null; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - object? IMillerLoopDriver.Square(in object? f) => null; + public static object? One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => null; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - object? IMillerLoopDriver.Conjugate(in object? f) => null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object? IMillerLoopDriver.Square(in object? f) => null; - object? IMillerLoopDriver.One - { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => null; - } + object? IMillerLoopDriver.Conjugate(in object? f) => null; + + object? IMillerLoopDriver.One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => null; + } - #endregion + #endregion + } } } diff --git a/src/Neo.Cryptography.BLS12_381/G2Prepared.cs b/src/Neo.Cryptography.BLS12_381/G2Prepared.cs index 38e8bdc902..422dd7c61e 100644 --- a/src/Neo.Cryptography.BLS12_381/G2Prepared.cs +++ b/src/Neo.Cryptography.BLS12_381/G2Prepared.cs @@ -12,20 +12,21 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.MillerLoopUtility; -namespace Neo.Cryptography.BLS12_381; - -partial class G2Prepared +namespace Neo.Cryptography.BLS12_381 { - public readonly bool Infinity; - public readonly List<(Fp2, Fp2, Fp2)> Coeffs; - - public G2Prepared(in G2Affine q) + partial class G2Prepared { - Infinity = q.IsIdentity; - var q2 = ConditionalSelect(in q, in G2Affine.Generator, Infinity); - var adder = new Adder(q2); - MillerLoop(adder); - Coeffs = adder.Coeffs; - if (Coeffs.Count != 68) throw new InvalidOperationException(); + public readonly bool Infinity; + public readonly List<(Fp2, Fp2, Fp2)> Coeffs; + + public G2Prepared(in G2Affine q) + { + Infinity = q.IsIdentity; + var q2 = ConditionalSelect(in q, in G2Affine.Generator, Infinity); + var adder = new Adder(q2); + MillerLoop(adder); + Coeffs = adder.Coeffs; + if (Coeffs.Count != 68) throw new InvalidOperationException(); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/G2Projective.cs b/src/Neo.Cryptography.BLS12_381/G2Projective.cs index 08e0fd4593..79cd941d48 100644 --- a/src/Neo.Cryptography.BLS12_381/G2Projective.cs +++ b/src/Neo.Cryptography.BLS12_381/G2Projective.cs @@ -17,361 +17,362 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.G2Constants; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Fp2.Size * 3)] -public readonly struct G2Projective : IEquatable +namespace Neo.Cryptography.BLS12_381 { - [FieldOffset(0)] - public readonly Fp2 X; - [FieldOffset(Fp2.Size)] - public readonly Fp2 Y; - [FieldOffset(Fp2.Size * 2)] - public readonly Fp2 Z; - - public static readonly G2Projective Identity = new(in Fp2.Zero, in Fp2.One, in Fp2.Zero); - public static readonly G2Projective Generator = new(in GeneratorX, in GeneratorY, in Fp2.One); - - public bool IsIdentity => Z.IsZero; - public bool IsOnCurve => ((Y.Square() * Z) == (X.Square() * X + Z.Square() * Z * B)) | Z.IsZero; // Y^2 Z = X^3 + b Z^3 - - public G2Projective(in Fp2 x, in Fp2 y, in Fp2 z) - { - X = x; - Y = y; - Z = z; - } - - public G2Projective(in G2Affine p) + [StructLayout(LayoutKind.Explicit, Size = Fp2.Size * 3)] + public readonly struct G2Projective : IEquatable { - X = p.X; - Y = p.Y; - Z = ConditionalSelect(in Fp2.One, in Fp2.Zero, p.Infinity); - } - - public static bool operator ==(in G2Projective a, in G2Projective b) - { - // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? - - var x1 = a.X * b.Z; - var x2 = b.X * a.Z; + [FieldOffset(0)] + public readonly Fp2 X; + [FieldOffset(Fp2.Size)] + public readonly Fp2 Y; + [FieldOffset(Fp2.Size * 2)] + public readonly Fp2 Z; - var y1 = a.Y * b.Z; - var y2 = b.Y * a.Z; + public static readonly G2Projective Identity = new(in Fp2.Zero, in Fp2.One, in Fp2.Zero); + public static readonly G2Projective Generator = new(in GeneratorX, in GeneratorY, in Fp2.One); - var self_is_zero = a.Z.IsZero; - var other_is_zero = b.Z.IsZero; + public bool IsIdentity => Z.IsZero; + public bool IsOnCurve => ((Y.Square() * Z) == (X.Square() * X + Z.Square() * Z * B)) | Z.IsZero; // Y^2 Z = X^3 + b Z^3 - return (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1 == x2 & y1 == y2); - // Neither point at infinity, coordinates are the same - } + public G2Projective(in Fp2 x, in Fp2 y, in Fp2 z) + { + X = x; + Y = y; + Z = z; + } - public static bool operator !=(in G2Projective a, in G2Projective b) - { - return !(a == b); - } + public G2Projective(in G2Affine p) + { + X = p.X; + Y = p.Y; + Z = ConditionalSelect(in Fp2.One, in Fp2.Zero, p.Infinity); + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not G2Projective other) return false; - return this == other; - } + public static bool operator ==(in G2Projective a, in G2Projective b) + { + // Is (xz, yz, z) equal to (x'z', y'z', z') when converted to affine? - public bool Equals(G2Projective other) - { - return this == other; - } + var x1 = a.X * b.Z; + var x2 = b.X * a.Z; - public override int GetHashCode() - { - return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); - } + var y1 = a.Y * b.Z; + var y2 = b.Y * a.Z; - public static G2Projective operator -(in G2Projective a) - { - return new(in a.X, -a.Y, in a.Z); - } + var self_is_zero = a.Z.IsZero; + var other_is_zero = b.Z.IsZero; - public static G2Projective operator +(in G2Projective a, in G2Projective b) - { - // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf - - var t0 = a.X * b.X; - var t1 = a.Y * b.Y; - var t2 = a.Z * b.Z; - var t3 = a.X + a.Y; - var t4 = b.X + b.Y; - t3 *= t4; - t4 = t0 + t1; - t3 -= t4; - t4 = a.Y + a.Z; - var x3 = b.Y + b.Z; - t4 *= x3; - x3 = t1 + t2; - t4 -= x3; - x3 = a.X + a.Z; - var y3 = b.X + b.Z; - x3 *= y3; - y3 = t0 + t2; - y3 = x3 - y3; - x3 = t0 + t0; - t0 = x3 + t0; - t2 = MulBy3B(t2); - var z3 = t1 + t2; - t1 -= t2; - y3 = MulBy3B(y3); - x3 = t4 * y3; - t2 = t3 * t1; - x3 = t2 - x3; - y3 *= t0; - t1 *= z3; - y3 = t1 + y3; - t0 *= t3; - z3 *= t4; - z3 += t0; - - return new G2Projective(in x3, in y3, in z3); - } + return (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1 == x2 & y1 == y2); + // Neither point at infinity, coordinates are the same + } - public static G2Projective operator +(in G2Affine a, in G2Projective b) - { - return b + a; - } + public static bool operator !=(in G2Projective a, in G2Projective b) + { + return !(a == b); + } - public static G2Projective operator +(in G2Projective a, in G2Affine b) - { - // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf - - var t0 = a.X * b.X; - var t1 = a.Y * b.Y; - var t3 = b.X + b.Y; - var t4 = a.X + a.Y; - t3 *= t4; - t4 = t0 + t1; - t3 -= t4; - t4 = b.Y * a.Z; - t4 += a.Y; - var y3 = b.X * a.Z; - y3 += a.X; - var x3 = t0 + t0; - t0 = x3 + t0; - var t2 = MulBy3B(a.Z); - var z3 = t1 + t2; - t1 -= t2; - y3 = MulBy3B(y3); - x3 = t4 * y3; - t2 = t3 * t1; - x3 = t2 - x3; - y3 *= t0; - t1 *= z3; - y3 = t1 + y3; - t0 *= t3; - z3 *= t4; - z3 += t0; - - var tmp = new G2Projective(in x3, in y3, in z3); - - return ConditionalSelect(in tmp, in a, b.IsIdentity); - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not G2Projective other) return false; + return this == other; + } - public static G2Projective operator -(in G2Projective a, in G2Projective b) - { - return a + -b; - } + public bool Equals(G2Projective other) + { + return this == other; + } - public static G2Projective operator -(in G2Affine a, in G2Projective b) - { - return a + -b; - } + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); + } - public static G2Projective operator -(in G2Projective a, in G2Affine b) - { - return a + -b; - } + public static G2Projective operator -(in G2Projective a) + { + return new(in a.X, -a.Y, in a.Z); + } - public static G2Projective operator *(in G2Projective a, in Scalar b) - { - return a * b.ToArray(); - } + public static G2Projective operator +(in G2Projective a, in G2Projective b) + { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + + var t0 = a.X * b.X; + var t1 = a.Y * b.Y; + var t2 = a.Z * b.Z; + var t3 = a.X + a.Y; + var t4 = b.X + b.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = a.Y + a.Z; + var x3 = b.Y + b.Z; + t4 *= x3; + x3 = t1 + t2; + t4 -= x3; + x3 = a.X + a.Z; + var y3 = b.X + b.Z; + x3 *= y3; + y3 = t0 + t2; + y3 = x3 - y3; + x3 = t0 + t0; + t0 = x3 + t0; + t2 = MulBy3B(t2); + var z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + return new G2Projective(in x3, in y3, in z3); + } - public static G2Projective operator *(in G2Projective a, byte[] b) - { - var acc = Identity; - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - foreach (bool bit in b - .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) - .Reverse() - .Skip(1)) + public static G2Projective operator +(in G2Affine a, in G2Projective b) { - acc = acc.Double(); - acc = ConditionalSelect(in acc, acc + a, bit); + return b + a; } - return acc; - } + public static G2Projective operator +(in G2Projective a, in G2Affine b) + { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + + var t0 = a.X * b.X; + var t1 = a.Y * b.Y; + var t3 = b.X + b.Y; + var t4 = a.X + a.Y; + t3 *= t4; + t4 = t0 + t1; + t3 -= t4; + t4 = b.Y * a.Z; + t4 += a.Y; + var y3 = b.X * a.Z; + y3 += a.X; + var x3 = t0 + t0; + t0 = x3 + t0; + var t2 = MulBy3B(a.Z); + var z3 = t1 + t2; + t1 -= t2; + y3 = MulBy3B(y3); + x3 = t4 * y3; + t2 = t3 * t1; + x3 = t2 - x3; + y3 *= t0; + t1 *= z3; + y3 = t1 + y3; + t0 *= t3; + z3 *= t4; + z3 += t0; + + var tmp = new G2Projective(in x3, in y3, in z3); + + return ConditionalSelect(in tmp, in a, b.IsIdentity); + } - private static Fp2 MulBy3B(Fp2 x) - { - return x * B3; - } + public static G2Projective operator -(in G2Projective a, in G2Projective b) + { + return a + -b; + } - public G2Projective Double() - { - // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf - - var t0 = Y.Square(); - var z3 = t0 + t0; - z3 += z3; - z3 += z3; - var t1 = Y * Z; - var t2 = Z.Square(); - t2 = MulBy3B(t2); - var x3 = t2 * z3; - var y3 = t0 + t2; - z3 = t1 * z3; - t1 = t2 + t2; - t2 = t1 + t2; - t0 -= t2; - y3 = t0 * y3; - y3 = x3 + y3; - t1 = X * Y; - x3 = t0 * t1; - x3 += x3; - - var tmp = new G2Projective(in x3, in y3, in z3); - - return ConditionalSelect(in tmp, in Identity, IsIdentity); - } + public static G2Projective operator -(in G2Affine a, in G2Projective b) + { + return a + -b; + } - internal G2Projective Psi() - { - return new G2Projective( - // x = frobenius(x)/((u+1)^((p-1)/3)) - X.FrobeniusMap() * PsiCoeffX, - // y = frobenius(y)/(u+1)^((p-1)/2) - Y.FrobeniusMap() * PsiCoeffY, - // z = frobenius(z) - Z.FrobeniusMap() - ); - } + public static G2Projective operator -(in G2Projective a, in G2Affine b) + { + return a + -b; + } - internal G2Projective Psi2() - { - return new G2Projective( - // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field. - X * Psi2CoeffX, - // y = -frobenius^2(y); note that q^2 is the order of the field. - -Y, - // z = z - in Z - ); - } + public static G2Projective operator *(in G2Projective a, in Scalar b) + { + return a * b.ToArray(); + } - internal G2Projective MulByX() - { - var xself = Identity; - // NOTE: in BLS12-381 we can just skip the first bit. - var x = BLS_X >> 1; - var acc = this; - while (x != 0) + public static G2Projective operator *(in G2Projective a, byte[] b) { - acc = acc.Double(); - if (x % 2 == 1) + var acc = Identity; + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + foreach (bool bit in b + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse() + .Skip(1)) { - xself += acc; + acc = acc.Double(); + acc = ConditionalSelect(in acc, acc + a, bit); } - x >>= 1; + + return acc; } - // finally, flip the sign - if (BLS_X_IS_NEGATIVE) + + private static Fp2 MulBy3B(Fp2 x) { - xself = -xself; + return x * B3; } - return xself; - } - public G2Projective ClearCofactor() - { - var t1 = MulByX(); // [x] P - var t2 = Psi(); // psi(P) - - return Double().Psi2() // psi^2(2P) - + (t1 + t2).MulByX() // psi^2(2P) + [x^2] P + [x] psi(P) - - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) - - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) - - this; // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) - } + public G2Projective Double() + { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + + var t0 = Y.Square(); + var z3 = t0 + t0; + z3 += z3; + z3 += z3; + var t1 = Y * Z; + var t2 = Z.Square(); + t2 = MulBy3B(t2); + var x3 = t2 * z3; + var y3 = t0 + t2; + z3 = t1 * z3; + t1 = t2 + t2; + t2 = t1 + t2; + t0 -= t2; + y3 = t0 * y3; + y3 = x3 + y3; + t1 = X * Y; + x3 = t0 * t1; + x3 += x3; + + var tmp = new G2Projective(in x3, in y3, in z3); + + return ConditionalSelect(in tmp, in Identity, IsIdentity); + } - public static void BatchNormalize(ReadOnlySpan p, Span q) - { - int length = p.Length; - if (length != q.Length) - throw new ArgumentException($"{nameof(p)} and {nameof(q)} must have the same length."); + internal G2Projective Psi() + { + return new G2Projective( + // x = frobenius(x)/((u+1)^((p-1)/3)) + X.FrobeniusMap() * PsiCoeffX, + // y = frobenius(y)/(u+1)^((p-1)/2) + Y.FrobeniusMap() * PsiCoeffY, + // z = frobenius(z) + Z.FrobeniusMap() + ); + } - Span x = stackalloc Fp2[length]; - Fp2 acc = Fp2.One; - for (int i = 0; i < length; i++) + internal G2Projective Psi2() { - // We use the `x` field of `G2Affine` to store the product - // of previous z-coordinates seen. - x[i] = acc; + return new G2Projective( + // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field. + X * Psi2CoeffX, + // y = -frobenius^2(y); note that q^2 is the order of the field. + -Y, + // z = z + in Z + ); + } - // We will end up skipping all identities in p - acc = ConditionalSelect(acc * p[i].Z, in acc, p[i].IsIdentity); + internal G2Projective MulByX() + { + var xself = Identity; + // NOTE: in BLS12-381 we can just skip the first bit. + var x = BLS_X >> 1; + var acc = this; + while (x != 0) + { + acc = acc.Double(); + if (x % 2 == 1) + { + xself += acc; + } + x >>= 1; + } + // finally, flip the sign + if (BLS_X_IS_NEGATIVE) + { + xself = -xself; + } + return xself; } - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.Invert(); + public G2Projective ClearCofactor() + { + var t1 = MulByX(); // [x] P + var t2 = Psi(); // psi(P) + + return Double().Psi2() // psi^2(2P) + + (t1 + t2).MulByX() // psi^2(2P) + [x^2] P + [x] psi(P) + - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) + - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) + - this; // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) + } - for (int i = length - 1; i >= 0; i--) + public static void BatchNormalize(ReadOnlySpan p, Span q) { - bool skip = p[i].IsIdentity; + int length = p.Length; + if (length != q.Length) + throw new ArgumentException($"{nameof(p)} and {nameof(q)} must have the same length."); - // Compute tmp = 1/z - var tmp = x[i] * acc; + Span x = stackalloc Fp2[length]; + Fp2 acc = Fp2.One; + for (int i = 0; i < length; i++) + { + // We use the `x` field of `G2Affine` to store the product + // of previous z-coordinates seen. + x[i] = acc; - // Cancel out z-coordinate in denominator of `acc` - acc = ConditionalSelect(acc * p[i].Z, in acc, skip); + // We will end up skipping all identities in p + acc = ConditionalSelect(acc * p[i].Z, in acc, p[i].IsIdentity); + } - // Set the coordinates to the correct value - G2Affine qi = new(p[i].X * tmp, p[i].Y * tmp); + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.Invert(); - q[i] = ConditionalSelect(in qi, in G2Affine.Identity, skip); - } - } + for (int i = length - 1; i >= 0; i--) + { + bool skip = p[i].IsIdentity; - public static G2Projective Random(RandomNumberGenerator rng) - { - Span buffer = stackalloc byte[sizeof(uint)]; - while (true) - { - var x = Fp2.Random(rng); - rng.GetBytes(buffer); - var flip_sign = BinaryPrimitives.ReadUInt32LittleEndian(buffer) % 2 != 0; + // Compute tmp = 1/z + var tmp = x[i] * acc; - // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) - var y = ((x.Square() * x) + B).Sqrt(); + // Cancel out z-coordinate in denominator of `acc` + acc = ConditionalSelect(acc * p[i].Z, in acc, skip); - G2Affine p; - try - { - p = new G2Affine(in x, flip_sign ? -y : y); + // Set the coordinates to the correct value + G2Affine qi = new(p[i].X * tmp, p[i].Y * tmp); + + q[i] = ConditionalSelect(in qi, in G2Affine.Identity, skip); } - catch + } + + public static G2Projective Random(RandomNumberGenerator rng) + { + Span buffer = stackalloc byte[sizeof(uint)]; + while (true) { - continue; + var x = Fp2.Random(rng); + rng.GetBytes(buffer); + var flip_sign = BinaryPrimitives.ReadUInt32LittleEndian(buffer) % 2 != 0; + + // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) + var y = ((x.Square() * x) + B).Sqrt(); + + G2Affine p; + try + { + p = new G2Affine(in x, flip_sign ? -y : y); + } + catch + { + continue; + } + + var result = p.ToCurve().ClearCofactor(); + if (!result.IsIdentity) return result; } - - var result = p.ToCurve().ClearCofactor(); - if (!result.IsIdentity) return result; } } } diff --git a/src/Neo.Cryptography.BLS12_381/Gt.cs b/src/Neo.Cryptography.BLS12_381/Gt.cs index e6c33b18d5..53732dbc87 100644 --- a/src/Neo.Cryptography.BLS12_381/Gt.cs +++ b/src/Neo.Cryptography.BLS12_381/Gt.cs @@ -15,121 +15,122 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.GtConstants; -namespace Neo.Cryptography.BLS12_381; - -public readonly struct Gt : IEquatable +namespace Neo.Cryptography.BLS12_381 { - public readonly Fp12 Value; - - public static readonly Gt Identity = new(in Fp12.One); - public static readonly Gt Generator = new(in GeneratorValue); + public readonly struct Gt : IEquatable + { + public readonly Fp12 Value; - public bool IsIdentity => this == Identity; + public static readonly Gt Identity = new(in Fp12.One); + public static readonly Gt Generator = new(in GeneratorValue); - public Gt(in Fp12 f) - { - Value = f; - } + public bool IsIdentity => this == Identity; - public static Gt FromBytes(ReadOnlySpan data) - { - return new(Fp12.FromBytes(data)); - } + public Gt(in Fp12 f) + { + Value = f; + } - public static bool operator ==(in Gt a, in Gt b) - { - return a.Value == b.Value; - } + public static Gt FromBytes(ReadOnlySpan data) + { + return new(Fp12.FromBytes(data)); + } - public static bool operator !=(in Gt a, in Gt b) - { - return !(a == b); - } + public static bool operator ==(in Gt a, in Gt b) + { + return a.Value == b.Value; + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not Gt other) return false; - return this == other; - } + public static bool operator !=(in Gt a, in Gt b) + { + return !(a == b); + } - public bool Equals(Gt other) - { - return this == other; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Gt other) return false; + return this == other; + } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + public bool Equals(Gt other) + { + return this == other; + } - public byte[] ToArray() - { - return Value.ToArray(); - } + public override int GetHashCode() + { + return Value.GetHashCode(); + } - public bool TryWrite(Span buffer) - { - return Value.TryWrite(buffer); - } + public byte[] ToArray() + { + return Value.ToArray(); + } - public static Gt Random(RandomNumberGenerator rng) - { - while (true) + public bool TryWrite(Span buffer) { - var inner = Fp12.Random(rng); + return Value.TryWrite(buffer); + } - // Not all elements of Fp12 are elements of the prime-order multiplicative - // subgroup. We run the random element through final_exponentiation to obtain - // a valid element, which requires that it is non-zero. - if (!inner.IsZero) + public static Gt Random(RandomNumberGenerator rng) + { + while (true) { - ref MillerLoopResult result = ref Unsafe.As(ref inner); - return result.FinalExponentiation(); + var inner = Fp12.Random(rng); + + // Not all elements of Fp12 are elements of the prime-order multiplicative + // subgroup. We run the random element through final_exponentiation to obtain + // a valid element, which requires that it is non-zero. + if (!inner.IsZero) + { + ref MillerLoopResult result = ref Unsafe.As(ref inner); + return result.FinalExponentiation(); + } } } - } - - public Gt Double() - { - return new(Value.Square()); - } - public static Gt operator -(in Gt a) - { - // The element is unitary, so we just conjugate. - return new(a.Value.Conjugate()); - } + public Gt Double() + { + return new(Value.Square()); + } - public static Gt operator +(in Gt a, in Gt b) - { - return new(a.Value * b.Value); - } + public static Gt operator -(in Gt a) + { + // The element is unitary, so we just conjugate. + return new(a.Value.Conjugate()); + } - public static Gt operator -(in Gt a, in Gt b) - { - return a + -b; - } + public static Gt operator +(in Gt a, in Gt b) + { + return new(a.Value * b.Value); + } - public static Gt operator *(in Gt a, in Scalar b) - { - var acc = Identity; - - // This is a simple double-and-add implementation of group element - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading bit because it's always unset for Fq - // elements. - foreach (bool bit in b - .ToArray() - .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) - .Reverse() - .Skip(1)) + public static Gt operator -(in Gt a, in Gt b) { - acc = acc.Double(); - acc = ConditionalSelect(in acc, acc + a, bit); + return a + -b; } - return acc; + public static Gt operator *(in Gt a, in Scalar b) + { + var acc = Identity; + + // This is a simple double-and-add implementation of group element + // multiplication, moving from most significant to least + // significant bit of the scalar. + // + // We skip the leading bit because it's always unset for Fq + // elements. + foreach (bool bit in b + .ToArray() + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse() + .Skip(1)) + { + acc = acc.Double(); + acc = ConditionalSelect(in acc, acc + a, bit); + } + + return acc; + } } } diff --git a/src/Neo.Cryptography.BLS12_381/GtConstants.cs b/src/Neo.Cryptography.BLS12_381/GtConstants.cs index ca76083fe8..26582d2d03 100644 --- a/src/Neo.Cryptography.BLS12_381/GtConstants.cs +++ b/src/Neo.Cryptography.BLS12_381/GtConstants.cs @@ -9,107 +9,108 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class GtConstants +namespace Neo.Cryptography.BLS12_381 { - // pairing(&G1Affine.generator(), &G2Affine.generator()) - public static readonly Fp12 GeneratorValue = new(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x1972_e433_a01f_85c5, - 0x97d3_2b76_fd77_2538, - 0xc8ce_546f_c96b_cdf9, - 0xcef6_3e73_66d4_0614, - 0xa611_3427_8184_3780, - 0x13f3_448a_3fc6_d825 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xd263_31b0_2e9d_6995, - 0x9d68_a482_f779_7e7d, - 0x9c9b_2924_8d39_ea92, - 0xf480_1ca2_e131_07aa, - 0xa16c_0732_bdbc_b066, - 0x083c_a4af_ba36_0478 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x59e2_61db_0916_b641, - 0x2716_b6f4_b23e_960d, - 0xc8e5_5b10_a0bd_9c45, - 0x0bdb_0bd9_9c4d_eda8, - 0x8cf8_9ebf_57fd_aac5, - 0x12d6_b792_9e77_7a5e - }), Fp.FromRawUnchecked(new ulong[] - { - 0x5fc8_5188_b0e1_5f35, - 0x34a0_6e3a_8f09_6365, - 0xdb31_26a6_e02a_d62c, - 0xfc6f_5aa9_7d9a_990b, - 0xa12f_55f5_eb89_c210, - 0x1723_703a_926f_8889 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x9358_8f29_7182_8778, - 0x43f6_5b86_11ab_7585, - 0x3183_aaf5_ec27_9fdf, - 0xfa73_d7e1_8ac9_9df6, - 0x64e1_76a6_a64c_99b0, - 0x179f_a78c_5838_8f1f - }), Fp.FromRawUnchecked(new ulong[] - { - 0x672a_0a11_ca2a_ef12, - 0x0d11_b9b5_2aa3_f16b, - 0xa444_12d0_699d_056e, - 0xc01d_0177_221a_5ba5, - 0x66e0_cede_6c73_5529, - 0x05f5_a71e_9fdd_c339 - }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xd30a_88a1_b062_c679, - 0x5ac5_6a5d_35fc_8304, - 0xd0c8_34a6_a81f_290d, - 0xcd54_30c2_da37_07c7, - 0xf0c2_7ff7_8050_0af0, - 0x0924_5da6_e2d7_2eae - }), - Fp.FromRawUnchecked(new ulong[] - { - 0x9f2e_0676_791b_5156, - 0xe2d1_c823_4918_fe13, - 0x4c9e_459f_3c56_1bf4, - 0xa3e8_5e53_b9d3_e3c1, - 0x820a_121e_21a7_0020, - 0x15af_6183_41c5_9acc - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x7c95_658c_2499_3ab1, - 0x73eb_3872_1ca8_86b9, - 0x5256_d749_4774_34bc, - 0x8ba4_1902_ea50_4a8b, - 0x04a3_d3f8_0c86_ce6d, - 0x18a6_4a87_fb68_6eaa - }), Fp.FromRawUnchecked(new ulong[] - { - 0xbb83_e71b_b920_cf26, - 0x2a52_77ac_92a7_3945, - 0xfc0e_e59f_94f0_46a0, - 0x7158_cdf3_7860_58f7, - 0x7cc1_061b_82f9_45f6, - 0x03f8_47aa_9fdb_e567 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x8078_dba5_6134_e657, - 0x1cd7_ec9a_4399_8a6e, - 0xb1aa_599a_1a99_3766, - 0xc9a0_f62f_0842_ee44, - 0x8e15_9be3_b605_dffa, - 0x0c86_ba0d_4af1_3fc2 - }), Fp.FromRawUnchecked(new ulong[] + static class GtConstants { - 0xe80f_f2a0_6a52_ffb1, - 0x7694_ca48_721a_906c, - 0x7583_183e_03b0_8514, - 0xf567_afdd_40ce_e4e2, - 0x9a6d_96d2_e526_a5fc, - 0x197e_9f49_861f_2242 - })))); + // pairing(&G1Affine.generator(), &G2Affine.generator()) + public static readonly Fp12 GeneratorValue = new(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1972_e433_a01f_85c5, + 0x97d3_2b76_fd77_2538, + 0xc8ce_546f_c96b_cdf9, + 0xcef6_3e73_66d4_0614, + 0xa611_3427_8184_3780, + 0x13f3_448a_3fc6_d825 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd263_31b0_2e9d_6995, + 0x9d68_a482_f779_7e7d, + 0x9c9b_2924_8d39_ea92, + 0xf480_1ca2_e131_07aa, + 0xa16c_0732_bdbc_b066, + 0x083c_a4af_ba36_0478 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x59e2_61db_0916_b641, + 0x2716_b6f4_b23e_960d, + 0xc8e5_5b10_a0bd_9c45, + 0x0bdb_0bd9_9c4d_eda8, + 0x8cf8_9ebf_57fd_aac5, + 0x12d6_b792_9e77_7a5e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x5fc8_5188_b0e1_5f35, + 0x34a0_6e3a_8f09_6365, + 0xdb31_26a6_e02a_d62c, + 0xfc6f_5aa9_7d9a_990b, + 0xa12f_55f5_eb89_c210, + 0x1723_703a_926f_8889 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x9358_8f29_7182_8778, + 0x43f6_5b86_11ab_7585, + 0x3183_aaf5_ec27_9fdf, + 0xfa73_d7e1_8ac9_9df6, + 0x64e1_76a6_a64c_99b0, + 0x179f_a78c_5838_8f1f + }), Fp.FromRawUnchecked(new ulong[] + { + 0x672a_0a11_ca2a_ef12, + 0x0d11_b9b5_2aa3_f16b, + 0xa444_12d0_699d_056e, + 0xc01d_0177_221a_5ba5, + 0x66e0_cede_6c73_5529, + 0x05f5_a71e_9fdd_c339 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xd30a_88a1_b062_c679, + 0x5ac5_6a5d_35fc_8304, + 0xd0c8_34a6_a81f_290d, + 0xcd54_30c2_da37_07c7, + 0xf0c2_7ff7_8050_0af0, + 0x0924_5da6_e2d7_2eae + }), + Fp.FromRawUnchecked(new ulong[] + { + 0x9f2e_0676_791b_5156, + 0xe2d1_c823_4918_fe13, + 0x4c9e_459f_3c56_1bf4, + 0xa3e8_5e53_b9d3_e3c1, + 0x820a_121e_21a7_0020, + 0x15af_6183_41c5_9acc + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x7c95_658c_2499_3ab1, + 0x73eb_3872_1ca8_86b9, + 0x5256_d749_4774_34bc, + 0x8ba4_1902_ea50_4a8b, + 0x04a3_d3f8_0c86_ce6d, + 0x18a6_4a87_fb68_6eaa + }), Fp.FromRawUnchecked(new ulong[] + { + 0xbb83_e71b_b920_cf26, + 0x2a52_77ac_92a7_3945, + 0xfc0e_e59f_94f0_46a0, + 0x7158_cdf3_7860_58f7, + 0x7cc1_061b_82f9_45f6, + 0x03f8_47aa_9fdb_e567 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x8078_dba5_6134_e657, + 0x1cd7_ec9a_4399_8a6e, + 0xb1aa_599a_1a99_3766, + 0xc9a0_f62f_0842_ee44, + 0x8e15_9be3_b605_dffa, + 0x0c86_ba0d_4af1_3fc2 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xe80f_f2a0_6a52_ffb1, + 0x7694_ca48_721a_906c, + 0x7583_183e_03b0_8514, + 0xf567_afdd_40ce_e4e2, + 0x9a6d_96d2_e526_a5fc, + 0x197e_9f49_861f_2242 + })))); + } } diff --git a/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs b/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs index 0d0d3ac9df..d5bced0763 100644 --- a/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs +++ b/src/Neo.Cryptography.BLS12_381/IMillerLoopDriver.cs @@ -9,13 +9,14 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -interface IMillerLoopDriver +namespace Neo.Cryptography.BLS12_381 { - public T DoublingStep(in T f); - public T AdditionStep(in T f); - public T Square(in T f); - public T Conjugate(in T f); - public T One { get; } + interface IMillerLoopDriver + { + public T DoublingStep(in T f); + public T AdditionStep(in T f); + public T Square(in T f); + public T Conjugate(in T f); + public T One { get; } + } } diff --git a/src/Neo.Cryptography.BLS12_381/INumber.cs b/src/Neo.Cryptography.BLS12_381/INumber.cs index f328a77c2e..1ba307b087 100644 --- a/src/Neo.Cryptography.BLS12_381/INumber.cs +++ b/src/Neo.Cryptography.BLS12_381/INumber.cs @@ -11,60 +11,61 @@ using System.Runtime.CompilerServices; -namespace Neo.Cryptography.BLS12_381; - -interface INumber where T : unmanaged, INumber +namespace Neo.Cryptography.BLS12_381 { - //static abstract int Size { get; } - //static abstract ref readonly T Zero { get; } - //static abstract ref readonly T One { get; } + interface INumber where T : unmanaged, INumber + { + //static abstract int Size { get; } + //static abstract ref readonly T Zero { get; } + //static abstract ref readonly T One { get; } - //static abstract T operator -(in T x); - //static abstract T operator +(in T x, in T y); - //static abstract T operator -(in T x, in T y); - //static abstract T operator *(in T x, in T y); + //static abstract T operator -(in T x); + //static abstract T operator +(in T x, in T y); + //static abstract T operator -(in T x, in T y); + //static abstract T operator *(in T x, in T y); - T Negate(); - T Sum(in T value); - T Subtract(in T value); - T Multiply(in T value); + T Negate(); + T Sum(in T value); + T Subtract(in T value); + T Multiply(in T value); - abstract T Square(); -} + abstract T Square(); + } -static class NumberExtensions -{ - private static T PowVartime(T one, T self, ulong[] by) where T : unmanaged, INumber + static class NumberExtensions { - // Although this is labeled "vartime", it is only - // variable time with respect to the exponent. - var res = one; - for (int j = by.Length - 1; j >= 0; j--) + private static T PowVartime(T one, T self, ulong[] by) where T : unmanaged, INumber { - for (int i = 63; i >= 0; i--) + // Although this is labeled "vartime", it is only + // variable time with respect to the exponent. + var res = one; + for (int j = by.Length - 1; j >= 0; j--) { - res = res.Square(); - if (((by[j] >> i) & 1) == 1) + for (int i = 63; i >= 0; i--) { - res = res.Multiply(self); + res = res.Square(); + if (((by[j] >> i) & 1) == 1) + { + res = res.Multiply(self); + } } } + return res; } - return res; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Fp PowVartime(this Fp self, ulong[] by) => PowVartime(Fp.One, self, by); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp PowVartime(this Fp self, ulong[] by) => PowVartime(Fp.One, self, by); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Fp2 PowVartime(this Fp2 self, ulong[] by) => PowVartime(Fp2.One, self, by); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp2 PowVartime(this Fp2 self, ulong[] by) => PowVartime(Fp2.One, self, by); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Fp6 PowVartime(this Fp6 self, ulong[] by) => PowVartime(Fp6.One, self, by); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp6 PowVartime(this Fp6 self, ulong[] by) => PowVartime(Fp6.One, self, by); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Fp12 PowVartime(this Fp12 self, ulong[] by) => PowVartime(Fp12.One, self, by); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Fp12 PowVartime(this Fp12 self, ulong[] by) => PowVartime(Fp12.One, self, by); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Scalar PowVartime(this Scalar self, ulong[] by) => PowVartime(Scalar.One, self, by); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Scalar PowVartime(this Scalar self, ulong[] by) => PowVartime(Scalar.One, self, by); + } } diff --git a/src/Neo.Cryptography.BLS12_381/MathUtility.cs b/src/Neo.Cryptography.BLS12_381/MathUtility.cs index 3721b482cc..8d7d23adb5 100644 --- a/src/Neo.Cryptography.BLS12_381/MathUtility.cs +++ b/src/Neo.Cryptography.BLS12_381/MathUtility.cs @@ -9,59 +9,60 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class MathUtility +namespace Neo.Cryptography.BLS12_381 { - public static (ulong result, ulong carry) Adc(ulong a, ulong b, ulong carry) + static class MathUtility { - ulong result = unchecked(a + b + carry); - carry = ((a & b) | ((a | b) & (~result))) >> 63; - return (result, carry); - } + public static (ulong result, ulong carry) Adc(ulong a, ulong b, ulong carry) + { + ulong result = unchecked(a + b + carry); + carry = ((a & b) | ((a | b) & (~result))) >> 63; + return (result, carry); + } - public static (ulong result, ulong borrow) Sbb(ulong a, ulong b, ulong borrow) - { - ulong result = unchecked(a - b - borrow); - borrow = (((~a) & b) | (~(a ^ b)) & result) >> 63; - return (result, borrow); - } + public static (ulong result, ulong borrow) Sbb(ulong a, ulong b, ulong borrow) + { + ulong result = unchecked(a - b - borrow); + borrow = (((~a) & b) | (~(a ^ b)) & result) >> 63; + return (result, borrow); + } - public static (ulong low, ulong high) Mac(ulong z, ulong x, ulong y, ulong carry) - { - ulong high = BigMul(x, y, out ulong low); - (low, carry) = Adc(low, carry, 0); - (high, _) = Adc(high, 0, carry); - (low, carry) = Adc(low, z, 0); - (high, _) = Adc(high, 0, carry); - return (low, high); - } + public static (ulong low, ulong high) Mac(ulong z, ulong x, ulong y, ulong carry) + { + ulong high = BigMul(x, y, out ulong low); + (low, carry) = Adc(low, carry, 0); + (high, _) = Adc(high, 0, carry); + (low, carry) = Adc(low, z, 0); + (high, _) = Adc(high, 0, carry); + return (low, high); + } - /// Produces the full product of two unsigned 64-bit numbers. - /// The first number to multiply. - /// The second number to multiply. - /// The low 64-bit of the product of the specified numbers. - /// The high 64-bit of the product of the specified numbers. - public static ulong BigMul(ulong a, ulong b, out ulong low) - { - // Adaptation of algorithm for multiplication - // of 32-bit unsigned integers described - // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 - // Basically, it's an optimized version of FOIL method applied to - // low and high dwords of each operand + /// Produces the full product of two unsigned 64-bit numbers. + /// The first number to multiply. + /// The second number to multiply. + /// The low 64-bit of the product of the specified numbers. + /// The high 64-bit of the product of the specified numbers. + public static ulong BigMul(ulong a, ulong b, out ulong low) + { + // Adaptation of algorithm for multiplication + // of 32-bit unsigned integers described + // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 + // Basically, it's an optimized version of FOIL method applied to + // low and high dwords of each operand - // Use 32-bit uints to optimize the fallback for 32-bit platforms. - uint al = (uint)a; - uint ah = (uint)(a >> 32); - uint bl = (uint)b; - uint bh = (uint)(b >> 32); + // Use 32-bit uints to optimize the fallback for 32-bit platforms. + uint al = (uint)a; + uint ah = (uint)(a >> 32); + uint bl = (uint)b; + uint bh = (uint)(b >> 32); - ulong mull = ((ulong)al) * bl; - ulong t = ((ulong)ah) * bl + (mull >> 32); - ulong tl = ((ulong)al) * bh + (uint)t; + ulong mull = ((ulong)al) * bl; + ulong t = ((ulong)ah) * bl + (mull >> 32); + ulong tl = ((ulong)al) * bh + (uint)t; - low = tl << 32 | (uint)mull; + low = tl << 32 | (uint)mull; - return ((ulong)ah) * bh + (t >> 32) + (tl >> 32); + return ((ulong)ah) * bh + (t >> 32) + (tl >> 32); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs b/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs index 7f9bdfbb60..b0625e3958 100644 --- a/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs +++ b/src/Neo.Cryptography.BLS12_381/MillerLoopResult.cs @@ -12,132 +12,133 @@ using System.Runtime.InteropServices; using static Neo.Cryptography.BLS12_381.Constants; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Fp12.Size)] -readonly struct MillerLoopResult +namespace Neo.Cryptography.BLS12_381 { - [FieldOffset(0)] - private readonly Fp12 v; - - public MillerLoopResult(in Fp12 v) + [StructLayout(LayoutKind.Explicit, Size = Fp12.Size)] + readonly struct MillerLoopResult { - this.v = v; - } + [FieldOffset(0)] + private readonly Fp12 v; - public Gt FinalExponentiation() - { - static (Fp2, Fp2) Fp4Square(in Fp2 a, in Fp2 b) + public MillerLoopResult(in Fp12 v) { - var t0 = a.Square(); - var t1 = b.Square(); - var t2 = t1.MulByNonresidue(); - var c0 = t2 + t0; - t2 = a + b; - t2 = t2.Square(); - t2 -= t0; - var c1 = t2 - t1; - - return (c0, c1); + this.v = v; } - static Fp12 CyclotomicSquare(in Fp12 f) + public Gt FinalExponentiation() { - var z0 = f.C0.C0; - var z4 = f.C0.C1; - var z3 = f.C0.C2; - var z2 = f.C1.C0; - var z1 = f.C1.C1; - var z5 = f.C1.C2; + static (Fp2, Fp2) Fp4Square(in Fp2 a, in Fp2 b) + { + var t0 = a.Square(); + var t1 = b.Square(); + var t2 = t1.MulByNonresidue(); + var c0 = t2 + t0; + t2 = a + b; + t2 = t2.Square(); + t2 -= t0; + var c1 = t2 - t1; + + return (c0, c1); + } - var (t0, t1) = Fp4Square(in z0, in z1); + static Fp12 CyclotomicSquare(in Fp12 f) + { + var z0 = f.C0.C0; + var z4 = f.C0.C1; + var z3 = f.C0.C2; + var z2 = f.C1.C0; + var z1 = f.C1.C1; + var z5 = f.C1.C2; - // For A - z0 = t0 - z0; - z0 = z0 + z0 + t0; + var (t0, t1) = Fp4Square(in z0, in z1); - z1 = t1 + z1; - z1 = z1 + z1 + t1; + // For A + z0 = t0 - z0; + z0 = z0 + z0 + t0; - (t0, t1) = Fp4Square(in z2, in z3); - var (t2, t3) = Fp4Square(in z4, in z5); + z1 = t1 + z1; + z1 = z1 + z1 + t1; - // For C - z4 = t0 - z4; - z4 = z4 + z4 + t0; + (t0, t1) = Fp4Square(in z2, in z3); + var (t2, t3) = Fp4Square(in z4, in z5); - z5 = t1 + z5; - z5 = z5 + z5 + t1; + // For C + z4 = t0 - z4; + z4 = z4 + z4 + t0; - // For B - t0 = t3.MulByNonresidue(); - z2 = t0 + z2; - z2 = z2 + z2 + t0; + z5 = t1 + z5; + z5 = z5 + z5 + t1; - z3 = t2 - z3; - z3 = z3 + z3 + t2; + // For B + t0 = t3.MulByNonresidue(); + z2 = t0 + z2; + z2 = z2 + z2 + t0; - return new Fp12(new Fp6(in z0, in z4, in z3), new Fp6(in z2, in z1, in z5)); - } + z3 = t2 - z3; + z3 = z3 + z3 + t2; - static Fp12 CycolotomicExp(in Fp12 f) - { - var x = BLS_X; - var tmp = Fp12.One; - var found_one = false; - foreach (bool i in Enumerable.Range(0, 64).Select(b => ((x >> b) & 1) == 1).Reverse()) - { - if (found_one) - tmp = CyclotomicSquare(tmp); - else - found_one = i; + return new Fp12(new Fp6(in z0, in z4, in z3), new Fp6(in z2, in z1, in z5)); + } - if (i) - tmp *= f; + static Fp12 CycolotomicExp(in Fp12 f) + { + var x = BLS_X; + var tmp = Fp12.One; + var found_one = false; + foreach (bool i in Enumerable.Range(0, 64).Select(b => ((x >> b) & 1) == 1).Reverse()) + { + if (found_one) + tmp = CyclotomicSquare(tmp); + else + found_one = i; + + if (i) + tmp *= f; + } + + return tmp.Conjugate(); } - return tmp.Conjugate(); + var f = v; + var t0 = f + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap(); + var t1 = f.Invert(); + var t2 = t0 * t1; + t1 = t2; + t2 = t2.FrobeniusMap().FrobeniusMap(); + t2 *= t1; + t1 = CyclotomicSquare(t2).Conjugate(); + var t3 = CycolotomicExp(t2); + var t4 = CyclotomicSquare(t3); + var t5 = t1 * t3; + t1 = CycolotomicExp(t5); + t0 = CycolotomicExp(t1); + var t6 = CycolotomicExp(t0); + t6 *= t4; + t4 = CycolotomicExp(t6); + t5 = t5.Conjugate(); + t4 *= t5 * t2; + t5 = t2.Conjugate(); + t1 *= t2; + t1 = t1.FrobeniusMap().FrobeniusMap().FrobeniusMap(); + t6 *= t5; + t6 = t6.FrobeniusMap(); + t3 *= t0; + t3 = t3.FrobeniusMap().FrobeniusMap(); + t3 *= t1; + t3 *= t6; + f = t3 * t4; + return new Gt(f); } - var f = v; - var t0 = f - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap(); - var t1 = f.Invert(); - var t2 = t0 * t1; - t1 = t2; - t2 = t2.FrobeniusMap().FrobeniusMap(); - t2 *= t1; - t1 = CyclotomicSquare(t2).Conjugate(); - var t3 = CycolotomicExp(t2); - var t4 = CyclotomicSquare(t3); - var t5 = t1 * t3; - t1 = CycolotomicExp(t5); - t0 = CycolotomicExp(t1); - var t6 = CycolotomicExp(t0); - t6 *= t4; - t4 = CycolotomicExp(t6); - t5 = t5.Conjugate(); - t4 *= t5 * t2; - t5 = t2.Conjugate(); - t1 *= t2; - t1 = t1.FrobeniusMap().FrobeniusMap().FrobeniusMap(); - t6 *= t5; - t6 = t6.FrobeniusMap(); - t3 *= t0; - t3 = t3.FrobeniusMap().FrobeniusMap(); - t3 *= t1; - t3 *= t6; - f = t3 * t4; - return new Gt(f); - } - - public static MillerLoopResult operator +(in MillerLoopResult a, in MillerLoopResult b) - { - return new(a.v * b.v); + public static MillerLoopResult operator +(in MillerLoopResult a, in MillerLoopResult b) + { + return new(a.v * b.v); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs b/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs index 03187b7944..171242e160 100644 --- a/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs +++ b/src/Neo.Cryptography.BLS12_381/MillerLoopUtility.cs @@ -11,110 +11,111 @@ using static Neo.Cryptography.BLS12_381.Constants; -namespace Neo.Cryptography.BLS12_381; - -static class MillerLoopUtility +namespace Neo.Cryptography.BLS12_381 { - public static T MillerLoop(D driver) where D : IMillerLoopDriver + static class MillerLoopUtility { - var f = driver.One; - - var found_one = false; - foreach (var i in Enumerable.Range(0, 64).Reverse().Select(b => ((BLS_X >> 1 >> b) & 1) == 1)) + public static T MillerLoop(D driver) where D : IMillerLoopDriver { - if (!found_one) + var f = driver.One; + + var found_one = false; + foreach (var i in Enumerable.Range(0, 64).Reverse().Select(b => ((BLS_X >> 1 >> b) & 1) == 1)) { - found_one = i; - continue; - } + if (!found_one) + { + found_one = i; + continue; + } - f = driver.DoublingStep(f); + f = driver.DoublingStep(f); - if (i) - f = driver.AdditionStep(f); + if (i) + f = driver.AdditionStep(f); - f = driver.Square(f); - } + f = driver.Square(f); + } - f = driver.DoublingStep(f); + f = driver.DoublingStep(f); - if (BLS_X_IS_NEGATIVE) - f = driver.Conjugate(f); + if (BLS_X_IS_NEGATIVE) + f = driver.Conjugate(f); - return f; - } + return f; + } - public static Fp12 Ell(in Fp12 f, in (Fp2 X, Fp2 Y, Fp2 Z) coeffs, in G1Affine p) - { - var c0 = new Fp2(coeffs.X.C0 * p.Y, coeffs.X.C1 * p.Y); - var c1 = new Fp2(coeffs.Y.C0 * p.X, coeffs.Y.C1 * p.X); - return f.MulBy_014(in coeffs.Z, in c1, in c0); - } + public static Fp12 Ell(in Fp12 f, in (Fp2 X, Fp2 Y, Fp2 Z) coeffs, in G1Affine p) + { + var c0 = new Fp2(coeffs.X.C0 * p.Y, coeffs.X.C1 * p.Y); + var c1 = new Fp2(coeffs.Y.C0 * p.X, coeffs.Y.C1 * p.X); + return f.MulBy_014(in coeffs.Z, in c1, in c0); + } - public static (Fp2, Fp2, Fp2) DoublingStep(ref G2Projective r) - { - // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf - var tmp0 = r.X.Square(); - var tmp1 = r.Y.Square(); - var tmp2 = tmp1.Square(); - var tmp3 = (tmp1 + r.X).Square() - tmp0 - tmp2; - tmp3 += tmp3; - var tmp4 = tmp0 + tmp0 + tmp0; - var tmp6 = r.X + tmp4; - var tmp5 = tmp4.Square(); - var zsquared = r.Z.Square(); - var x = tmp5 - tmp3 - tmp3; - var z = (r.Z + r.Y).Square() - tmp1 - zsquared; - var y = (tmp3 - x) * tmp4; - tmp2 += tmp2; - tmp2 += tmp2; - tmp2 += tmp2; - y -= tmp2; - r = new(in x, in y, in z); - tmp3 = tmp4 * zsquared; - tmp3 += tmp3; - tmp3 = -tmp3; - tmp6 = tmp6.Square() - tmp0 - tmp5; - tmp1 += tmp1; - tmp1 += tmp1; - tmp6 -= tmp1; - tmp0 = r.Z * zsquared; - tmp0 += tmp0; + public static (Fp2, Fp2, Fp2) DoublingStep(ref G2Projective r) + { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + var tmp0 = r.X.Square(); + var tmp1 = r.Y.Square(); + var tmp2 = tmp1.Square(); + var tmp3 = (tmp1 + r.X).Square() - tmp0 - tmp2; + tmp3 += tmp3; + var tmp4 = tmp0 + tmp0 + tmp0; + var tmp6 = r.X + tmp4; + var tmp5 = tmp4.Square(); + var zsquared = r.Z.Square(); + var x = tmp5 - tmp3 - tmp3; + var z = (r.Z + r.Y).Square() - tmp1 - zsquared; + var y = (tmp3 - x) * tmp4; + tmp2 += tmp2; + tmp2 += tmp2; + tmp2 += tmp2; + y -= tmp2; + r = new(in x, in y, in z); + tmp3 = tmp4 * zsquared; + tmp3 += tmp3; + tmp3 = -tmp3; + tmp6 = tmp6.Square() - tmp0 - tmp5; + tmp1 += tmp1; + tmp1 += tmp1; + tmp6 -= tmp1; + tmp0 = r.Z * zsquared; + tmp0 += tmp0; - return (tmp0, tmp3, tmp6); - } + return (tmp0, tmp3, tmp6); + } - public static (Fp2, Fp2, Fp2) AdditionStep(ref G2Projective r, in G2Affine q) - { - // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf - var zsquared = r.Z.Square(); - var ysquared = q.Y.Square(); - var t0 = zsquared * q.X; - var t1 = ((q.Y + r.Z).Square() - ysquared - zsquared) * zsquared; - var t2 = t0 - r.X; - var t3 = t2.Square(); - var t4 = t3 + t3; - t4 += t4; - var t5 = t4 * t2; - var t6 = t1 - r.Y - r.Y; - var t9 = t6 * q.X; - var t7 = t4 * r.X; - var x = t6.Square() - t5 - t7 - t7; - var z = (r.Z + t2).Square() - zsquared - t3; - var t10 = q.Y + z; - var t8 = (t7 - x) * t6; - t0 = r.Y * t5; - t0 += t0; - var y = t8 - t0; - r = new(in x, in y, in z); - t10 = t10.Square() - ysquared; - var ztsquared = r.Z.Square(); - t10 -= ztsquared; - t9 = t9 + t9 - t10; - t10 = r.Z + r.Z; - t6 = -t6; - t1 = t6 + t6; + public static (Fp2, Fp2, Fp2) AdditionStep(ref G2Projective r, in G2Affine q) + { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + var zsquared = r.Z.Square(); + var ysquared = q.Y.Square(); + var t0 = zsquared * q.X; + var t1 = ((q.Y + r.Z).Square() - ysquared - zsquared) * zsquared; + var t2 = t0 - r.X; + var t3 = t2.Square(); + var t4 = t3 + t3; + t4 += t4; + var t5 = t4 * t2; + var t6 = t1 - r.Y - r.Y; + var t9 = t6 * q.X; + var t7 = t4 * r.X; + var x = t6.Square() - t5 - t7 - t7; + var z = (r.Z + t2).Square() - zsquared - t3; + var t10 = q.Y + z; + var t8 = (t7 - x) * t6; + t0 = r.Y * t5; + t0 += t0; + var y = t8 - t0; + r = new(in x, in y, in z); + t10 = t10.Square() - ysquared; + var ztsquared = r.Z.Square(); + t10 -= ztsquared; + t9 = t9 + t9 - t10; + t10 = r.Z + r.Z; + t6 = -t6; + t1 = t6 + t6; - return (t10, t1, t9); + return (t10, t1, t9); + } } } diff --git a/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj index f74c9f1ec0..83cb016cbc 100644 --- a/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj +++ b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj @@ -2,7 +2,7 @@ 0.3.0 - netstandard2.1;net8.0 + netstandard2.1;net9.0 enable enable Neo.Cryptography.BLS12_381 @@ -10,7 +10,11 @@ - + + + + + diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index ef40e9b817..a26b7bf67d 100644 --- a/src/Neo.Cryptography.BLS12_381/Scalar.cs +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -18,496 +19,497 @@ using static Neo.Cryptography.BLS12_381.MathUtility; using static Neo.Cryptography.BLS12_381.ScalarConstants; -namespace Neo.Cryptography.BLS12_381; - -[StructLayout(LayoutKind.Explicit, Size = Size)] -public readonly struct Scalar : IEquatable, INumber +namespace Neo.Cryptography.BLS12_381 { - public const int Size = 32; - public const int SizeL = Size / sizeof(ulong); - public static readonly Scalar Default = new(); - - public static ref readonly Scalar Zero => ref Default; - public static ref readonly Scalar One => ref R; - - public bool IsZero => this == Zero; - - internal Scalar(ulong[] values) + [StructLayout(LayoutKind.Explicit, Size = Size)] + public readonly struct Scalar : IEquatable, INumber { - if (values.Length != SizeL) - throw new FormatException($"The argument `{nameof(values)}` must contain {SizeL} entries."); + public const int Size = 32; + public const int SizeL = Size / sizeof(ulong); + public static readonly Scalar Default = new(); - // This internal method is only used by the constants classes. - // The data must be in the correct format. - // So, there is no need to do any additional checks. - this = Unsafe.As(ref MemoryMarshal.GetReference(MemoryMarshal.Cast(values))); - } + public static ref readonly Scalar Zero => ref Default; + public static ref readonly Scalar One => ref R; - public Scalar(ulong value) - { - Span data = stackalloc ulong[SizeL]; - data[0] = value; - this = FromRaw(data); - } + public bool IsZero => this == Zero; - public Scalar(RandomNumberGenerator rng) - { - Span buffer = stackalloc byte[Size * 2]; - rng.GetBytes(buffer); - this = FromBytesWide(buffer); - } + internal Scalar(ulong[] values) + { + if (values.Length != SizeL) + throw new FormatException($"The argument `{nameof(values)}` must contain {SizeL} entries."); - public static Scalar FromBytes(ReadOnlySpan data) - { - if (data.Length != Size) - throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + // This internal method is only used by the constants classes. + // The data must be in the correct format. + // So, there is no need to do any additional checks. + this = Unsafe.As(ref MemoryMarshal.GetReference(MemoryMarshal.Cast(values))); + } - ref readonly Scalar ref_ = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + public Scalar(ulong value) + { + Span data = stackalloc ulong[SizeL]; + data[0] = value; + this = FromRaw(data); + } - try + public Scalar(RandomNumberGenerator rng) { - return ref_ * R2; + Span buffer = stackalloc byte[Size * 2]; + rng.GetBytes(buffer); + this = FromBytesWide(buffer); } - finally + + public static Scalar FromBytes(ReadOnlySpan data) { - ReadOnlySpan u64 = MemoryMarshal.Cast(data); - ulong borrow = 0; - (_, borrow) = Sbb(u64[0], MODULUS_LIMBS_64[0], borrow); - (_, borrow) = Sbb(u64[1], MODULUS_LIMBS_64[1], borrow); - (_, borrow) = Sbb(u64[2], MODULUS_LIMBS_64[2], borrow); - (_, borrow) = Sbb(u64[3], MODULUS_LIMBS_64[3], borrow); - if (borrow == 0) + if (data.Length != Size) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size} bytes."); + + ref readonly Scalar ref_ = ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + + try { - // If the element is smaller than MODULUS then the subtraction will underflow. - // Otherwise, throws. - // Why not throw before return? - // Because we want to run the method in a constant time. - throw new FormatException(); + return ref_ * R2; + } + finally + { + ReadOnlySpan u64 = MemoryMarshal.Cast(data); + ulong borrow = 0; + (_, borrow) = Sbb(u64[0], MODULUS_LIMBS_64[0], borrow); + (_, borrow) = Sbb(u64[1], MODULUS_LIMBS_64[1], borrow); + (_, borrow) = Sbb(u64[2], MODULUS_LIMBS_64[2], borrow); + (_, borrow) = Sbb(u64[3], MODULUS_LIMBS_64[3], borrow); + if (borrow == 0) + { + // If the element is smaller than MODULUS then the subtraction will underflow. + // Otherwise, throws. + // Why not throw before return? + // Because we want to run the method in a constant time. + throw new FormatException(); + } } } - } - public static Scalar FromBytesWide(ReadOnlySpan data) - { - if (data.Length != Size * 2) - throw new FormatException($"The argument `{nameof(data)}` must contain {Size * 2} bytes."); + public static Scalar FromBytesWide(ReadOnlySpan data) + { + if (data.Length != Size * 2) + throw new FormatException($"The argument `{nameof(data)}` must contain {Size * 2} bytes."); - ReadOnlySpan d = MemoryMarshal.Cast(data); - return d[0] * R2 + d[1] * R3; - } + ReadOnlySpan d = MemoryMarshal.Cast(data); + return d[0] * R2 + d[1] * R3; + } - public static Scalar FromRaw(ReadOnlySpan data) - { - if (data.Length != SizeL) - throw new FormatException($"The argument `{nameof(data)}` must contain {SizeL} entries."); + public static Scalar FromRaw(ReadOnlySpan data) + { + if (data.Length != SizeL) + throw new FormatException($"The argument `{nameof(data)}` must contain {SizeL} entries."); - ReadOnlySpan span = MemoryMarshal.Cast(data); - return span[0] * R2; - } + ReadOnlySpan span = MemoryMarshal.Cast(data); + return span[0] * R2; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReadOnlySpan GetSpan() - { - return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan GetSpan() + { + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Span GetSpanU64() - { - return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetSpanU64() + { + return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); + } - public override string ToString() - { - byte[] bytes = ToArray(); - StringBuilder sb = new(); - sb.Append("0x"); - for (int i = bytes.Length - 1; i >= 0; i--) - sb.AppendFormat("{0:x2}", bytes[i]); - return sb.ToString(); - } + public override string ToString() + { + var bytes = ToArray(); - public static bool operator ==(in Scalar a, in Scalar b) - { - return ConstantTimeEq(in a, in b); - } + StringBuilder sb = new(2 + (bytes.Length * 2)); + sb.Append("0x"); + sb.Append(bytes.ToHexString(true)); + return sb.ToString(); + } - public static bool operator !=(in Scalar a, in Scalar b) - { - return !(a == b); - } + public static bool operator ==(in Scalar a, in Scalar b) + { + return ConstantTimeEq(in a, in b); + } - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj is not Scalar other) return false; - return this == other; - } + public static bool operator !=(in Scalar a, in Scalar b) + { + return !(a == b); + } - public bool Equals(Scalar other) - { - return this == other; - } + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj is not Scalar other) return false; + return this == other; + } - public override int GetHashCode() - { - return base.GetHashCode(); - } + public bool Equals(Scalar other) + { + return this == other; + } - public Scalar Double() - { - return this + this; - } + public override int GetHashCode() + { + return base.GetHashCode(); + } - public byte[] ToArray() - { - ReadOnlySpan self = GetSpanU64(); + public Scalar Double() + { + return this + this; + } - // Turn into canonical form by computing - // (a.R) / R = a - Scalar result = MontgomeryReduce(self[0], self[1], self[2], self[3], 0, 0, 0, 0); - return result.GetSpan().ToArray(); - } + public byte[] ToArray() + { + ReadOnlySpan self = GetSpanU64(); - public Scalar Square() - { - ReadOnlySpan self = GetSpanU64(); - ulong r0, r1, r2, r3, r4, r5, r6, r7; - ulong carry; - - (r1, carry) = Mac(0, self[0], self[1], 0); - (r2, carry) = Mac(0, self[0], self[2], carry); - (r3, r4) = Mac(0, self[0], self[3], carry); - - (r3, carry) = Mac(r3, self[1], self[2], 0); - (r4, r5) = Mac(r4, self[1], self[3], carry); - - (r5, r6) = Mac(r5, self[2], self[3], 0); - - r7 = r6 >> 63; - r6 = (r6 << 1) | (r5 >> 63); - r5 = (r5 << 1) | (r4 >> 63); - r4 = (r4 << 1) | (r3 >> 63); - r3 = (r3 << 1) | (r2 >> 63); - r2 = (r2 << 1) | (r1 >> 63); - r1 <<= 1; - - (r0, carry) = Mac(0, self[0], self[0], 0); - (r1, carry) = Adc(r1, carry, 0); - (r2, carry) = Mac(r2, self[1], self[1], carry); - (r3, carry) = Adc(r3, carry, 0); - (r4, carry) = Mac(r4, self[2], self[2], carry); - (r5, carry) = Adc(r5, carry, 0); - (r6, carry) = Mac(r6, self[3], self[3], carry); - (r7, _) = Adc(r7, carry, 0); - - return MontgomeryReduce(r0, r1, r2, r3, r4, r5, r6, r7); - } + // Turn into canonical form by computing + // (a.R) / R = a + Scalar result = MontgomeryReduce(self[0], self[1], self[2], self[3], 0, 0, 0, 0); + return result.GetSpan().ToArray(); + } - public Scalar Sqrt() - { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + public Scalar Square() + { + ReadOnlySpan self = GetSpanU64(); + ulong r0, r1, r2, r3, r4, r5, r6, r7; + ulong carry; + + (r1, carry) = Mac(0, self[0], self[1], 0); + (r2, carry) = Mac(0, self[0], self[2], carry); + (r3, r4) = Mac(0, self[0], self[3], carry); + + (r3, carry) = Mac(r3, self[1], self[2], 0); + (r4, r5) = Mac(r4, self[1], self[3], carry); + + (r5, r6) = Mac(r5, self[2], self[3], 0); + + r7 = r6 >> 63; + r6 = (r6 << 1) | (r5 >> 63); + r5 = (r5 << 1) | (r4 >> 63); + r4 = (r4 << 1) | (r3 >> 63); + r3 = (r3 << 1) | (r2 >> 63); + r2 = (r2 << 1) | (r1 >> 63); + r1 <<= 1; + + (r0, carry) = Mac(0, self[0], self[0], 0); + (r1, carry) = Adc(r1, carry, 0); + (r2, carry) = Mac(r2, self[1], self[1], carry); + (r3, carry) = Adc(r3, carry, 0); + (r4, carry) = Mac(r4, self[2], self[2], carry); + (r5, carry) = Adc(r5, carry, 0); + (r6, carry) = Mac(r6, self[3], self[3], carry); + (r7, _) = Adc(r7, carry, 0); + + return MontgomeryReduce(r0, r1, r2, r3, r4, r5, r6, r7); + } - // w = self^((t - 1) // 2) - // = self^6104339283789297388802252303364915521546564123189034618274734669823 - var w = this.PowVartime(new ulong[] + public Scalar Sqrt() { - 0x7fff_2dff_7fff_ffff, - 0x04d0_ec02_a9de_d201, - 0x94ce_bea4_199c_ec04, - 0x0000_0000_39f6_d3a9 - }); + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - var v = S; - var x = this * w; - var b = x * w; + // w = self^((t - 1) // 2) + // = self^6104339283789297388802252303364915521546564123189034618274734669823 + var w = this.PowVartime(new ulong[] + { + 0x7fff_2dff_7fff_ffff, + 0x04d0_ec02_a9de_d201, + 0x94ce_bea4_199c_ec04, + 0x0000_0000_39f6_d3a9 + }); - // Initialize z as the 2^S root of unity. - var z = ROOT_OF_UNITY; + var v = S; + var x = this * w; + var b = x * w; - for (uint max_v = S; max_v >= 1; max_v--) - { - uint k = 1; - var tmp = b.Square(); - var j_less_than_v = true; + // Initialize z as the 2^S root of unity. + var z = ROOT_OF_UNITY; - for (uint j = 2; j < max_v; j++) + for (uint max_v = S; max_v >= 1; max_v--) { - var tmp_is_one = tmp == One; - var squared = ConditionalSelect(in tmp, in z, tmp_is_one).Square(); - tmp = ConditionalSelect(in squared, in tmp, tmp_is_one); - var new_z = ConditionalSelect(in z, in squared, tmp_is_one); - j_less_than_v &= j != v; - k = ConditionalSelect(j, k, tmp_is_one); - z = ConditionalSelect(in z, in new_z, j_less_than_v); + uint k = 1; + var tmp = b.Square(); + var j_less_than_v = true; + + for (uint j = 2; j < max_v; j++) + { + var tmp_is_one = tmp == One; + var squared = ConditionalSelect(in tmp, in z, tmp_is_one).Square(); + tmp = ConditionalSelect(in squared, in tmp, tmp_is_one); + var new_z = ConditionalSelect(in z, in squared, tmp_is_one); + j_less_than_v &= j != v; + k = ConditionalSelect(j, k, tmp_is_one); + z = ConditionalSelect(in z, in new_z, j_less_than_v); + } + + var result = x * z; + x = ConditionalSelect(in result, in x, b == One); + z = z.Square(); + b *= z; + v = k; } - var result = x * z; - x = ConditionalSelect(in result, in x, b == One); - z = z.Square(); - b *= z; - v = k; + if (x * x != this) throw new ArithmeticException(); + return x; } - if (x * x != this) throw new ArithmeticException(); - return x; - } - - public Scalar Pow(ulong[] by) - { - if (by.Length != SizeL) - throw new ArgumentException($"The length of the parameter `{nameof(by)}` must be {SizeL}."); - - var res = One; - for (int j = by.Length - 1; j >= 0; j--) + public Scalar Pow(ulong[] by) { - for (int i = 63; i >= 0; i--) + if (by.Length != SizeL) + throw new ArgumentException($"The length of the parameter `{nameof(by)}` must be {SizeL}."); + + var res = One; + for (int j = by.Length - 1; j >= 0; j--) { - res = res.Square(); - var tmp = res; - tmp *= this; - res.ConditionalAssign(in tmp, ((by[j] >> i) & 1) == 1); + for (int i = 63; i >= 0; i--) + { + res = res.Square(); + var tmp = res; + tmp *= this; + res.ConditionalAssign(in tmp, ((by[j] >> i) & 1) == 1); + } } + return res; } - return res; - } - public Scalar Invert() - { - static void SquareAssignMulti(ref Scalar n, int num_times) + public Scalar Invert() { - for (int i = 0; i < num_times; i++) + static void SquareAssignMulti(ref Scalar n, int num_times) { - n = n.Square(); + for (int i = 0; i < num_times; i++) + { + n = n.Square(); + } } - } - var t0 = Square(); - var t1 = t0 * this; - var t16 = t0.Square(); - var t6 = t16.Square(); - var t5 = t6 * t0; - t0 = t6 * t16; - var t12 = t5 * t16; - var t2 = t6.Square(); - var t7 = t5 * t6; - var t15 = t0 * t5; - var t17 = t12.Square(); - t1 *= t17; - var t3 = t7 * t2; - var t8 = t1 * t17; - var t4 = t8 * t2; - var t9 = t8 * t7; - t7 = t4 * t5; - var t11 = t4 * t17; - t5 = t9 * t17; - var t14 = t7 * t15; - var t13 = t11 * t12; - t12 = t11 * t17; - t15 *= t12; - t16 *= t15; - t3 *= t16; - t17 *= t3; - t0 *= t17; - t6 *= t0; - t2 *= t6; - SquareAssignMulti(ref t0, 8); - t0 *= t17; - SquareAssignMulti(ref t0, 9); - t0 *= t16; - SquareAssignMulti(ref t0, 9); - t0 *= t15; - SquareAssignMulti(ref t0, 9); - t0 *= t15; - SquareAssignMulti(ref t0, 7); - t0 *= t14; - SquareAssignMulti(ref t0, 7); - t0 *= t13; - SquareAssignMulti(ref t0, 10); - t0 *= t12; - SquareAssignMulti(ref t0, 9); - t0 *= t11; - SquareAssignMulti(ref t0, 8); - t0 *= t8; - SquareAssignMulti(ref t0, 8); - t0 *= this; - SquareAssignMulti(ref t0, 14); - t0 *= t9; - SquareAssignMulti(ref t0, 10); - t0 *= t8; - SquareAssignMulti(ref t0, 15); - t0 *= t7; - SquareAssignMulti(ref t0, 10); - t0 *= t6; - SquareAssignMulti(ref t0, 8); - t0 *= t5; - SquareAssignMulti(ref t0, 16); - t0 *= t3; - SquareAssignMulti(ref t0, 8); - t0 *= t2; - SquareAssignMulti(ref t0, 7); - t0 *= t4; - SquareAssignMulti(ref t0, 9); - t0 *= t2; - SquareAssignMulti(ref t0, 8); - t0 *= t3; - SquareAssignMulti(ref t0, 8); - t0 *= t2; - SquareAssignMulti(ref t0, 8); - t0 *= t2; - SquareAssignMulti(ref t0, 8); - t0 *= t2; - SquareAssignMulti(ref t0, 8); - t0 *= t3; - SquareAssignMulti(ref t0, 8); - t0 *= t2; - SquareAssignMulti(ref t0, 8); - t0 *= t2; - SquareAssignMulti(ref t0, 5); - t0 *= t1; - SquareAssignMulti(ref t0, 5); - t0 *= t1; - - if (this == Zero) throw new DivideByZeroException(); - return t0; - } + var t0 = Square(); + var t1 = t0 * this; + var t16 = t0.Square(); + var t6 = t16.Square(); + var t5 = t6 * t0; + t0 = t6 * t16; + var t12 = t5 * t16; + var t2 = t6.Square(); + var t7 = t5 * t6; + var t15 = t0 * t5; + var t17 = t12.Square(); + t1 *= t17; + var t3 = t7 * t2; + var t8 = t1 * t17; + var t4 = t8 * t2; + var t9 = t8 * t7; + t7 = t4 * t5; + var t11 = t4 * t17; + t5 = t9 * t17; + var t14 = t7 * t15; + var t13 = t11 * t12; + t12 = t11 * t17; + t15 *= t12; + t16 *= t15; + t3 *= t16; + t17 *= t3; + t0 *= t17; + t6 *= t0; + t2 *= t6; + SquareAssignMulti(ref t0, 8); + t0 *= t17; + SquareAssignMulti(ref t0, 9); + t0 *= t16; + SquareAssignMulti(ref t0, 9); + t0 *= t15; + SquareAssignMulti(ref t0, 9); + t0 *= t15; + SquareAssignMulti(ref t0, 7); + t0 *= t14; + SquareAssignMulti(ref t0, 7); + t0 *= t13; + SquareAssignMulti(ref t0, 10); + t0 *= t12; + SquareAssignMulti(ref t0, 9); + t0 *= t11; + SquareAssignMulti(ref t0, 8); + t0 *= t8; + SquareAssignMulti(ref t0, 8); + t0 *= this; + SquareAssignMulti(ref t0, 14); + t0 *= t9; + SquareAssignMulti(ref t0, 10); + t0 *= t8; + SquareAssignMulti(ref t0, 15); + t0 *= t7; + SquareAssignMulti(ref t0, 10); + t0 *= t6; + SquareAssignMulti(ref t0, 8); + t0 *= t5; + SquareAssignMulti(ref t0, 16); + t0 *= t3; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 7); + t0 *= t4; + SquareAssignMulti(ref t0, 9); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t3; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t3; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 8); + t0 *= t2; + SquareAssignMulti(ref t0, 5); + t0 *= t1; + SquareAssignMulti(ref t0, 5); + t0 *= t1; + + if (this == Zero) throw new DivideByZeroException(); + return t0; + } - private static Scalar MontgomeryReduce(ulong r0, ulong r1, ulong r2, ulong r3, ulong r4, ulong r5, ulong r6, ulong r7) - { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - ulong carry, carry2; - - var k = unchecked(r0 * INV); - (_, carry) = Mac(r0, k, MODULUS_LIMBS_64[0], 0); - (r1, carry) = Mac(r1, k, MODULUS_LIMBS_64[1], carry); - (r2, carry) = Mac(r2, k, MODULUS_LIMBS_64[2], carry); - (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[3], carry); - (r4, carry2) = Adc(r4, 0, carry); - - k = unchecked(r1 * INV); - (_, carry) = Mac(r1, k, MODULUS_LIMBS_64[0], 0); - (r2, carry) = Mac(r2, k, MODULUS_LIMBS_64[1], carry); - (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[2], carry); - (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[3], carry); - (r5, carry2) = Adc(r5, carry2, carry); - - k = unchecked(r2 * INV); - (_, carry) = Mac(r2, k, MODULUS_LIMBS_64[0], 0); - (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[1], carry); - (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[2], carry); - (r5, carry) = Mac(r5, k, MODULUS_LIMBS_64[3], carry); - (r6, carry2) = Adc(r6, carry2, carry); - - k = unchecked(r3 * INV); - (_, carry) = Mac(r3, k, MODULUS_LIMBS_64[0], 0); - (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[1], carry); - (r5, carry) = Mac(r5, k, MODULUS_LIMBS_64[2], carry); - (r6, carry) = Mac(r6, k, MODULUS_LIMBS_64[3], carry); - (r7, _) = Adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - ReadOnlySpan tmp = stackalloc[] { r4, r5, r6, r7 }; - return MemoryMarshal.Cast(tmp)[0] - MODULUS; - } + private static Scalar MontgomeryReduce(ulong r0, ulong r1, ulong r2, ulong r3, ulong r4, ulong r5, ulong r6, ulong r7) + { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + ulong carry, carry2; + + var k = unchecked(r0 * INV); + (_, carry) = Mac(r0, k, MODULUS_LIMBS_64[0], 0); + (r1, carry) = Mac(r1, k, MODULUS_LIMBS_64[1], carry); + (r2, carry) = Mac(r2, k, MODULUS_LIMBS_64[2], carry); + (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[3], carry); + (r4, carry2) = Adc(r4, 0, carry); + + k = unchecked(r1 * INV); + (_, carry) = Mac(r1, k, MODULUS_LIMBS_64[0], 0); + (r2, carry) = Mac(r2, k, MODULUS_LIMBS_64[1], carry); + (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[2], carry); + (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[3], carry); + (r5, carry2) = Adc(r5, carry2, carry); + + k = unchecked(r2 * INV); + (_, carry) = Mac(r2, k, MODULUS_LIMBS_64[0], 0); + (r3, carry) = Mac(r3, k, MODULUS_LIMBS_64[1], carry); + (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[2], carry); + (r5, carry) = Mac(r5, k, MODULUS_LIMBS_64[3], carry); + (r6, carry2) = Adc(r6, carry2, carry); + + k = unchecked(r3 * INV); + (_, carry) = Mac(r3, k, MODULUS_LIMBS_64[0], 0); + (r4, carry) = Mac(r4, k, MODULUS_LIMBS_64[1], carry); + (r5, carry) = Mac(r5, k, MODULUS_LIMBS_64[2], carry); + (r6, carry) = Mac(r6, k, MODULUS_LIMBS_64[3], carry); + (r7, _) = Adc(r7, carry2, carry); + + // Result may be within MODULUS of the correct value + ReadOnlySpan tmp = stackalloc[] { r4, r5, r6, r7 }; + return MemoryMarshal.Cast(tmp)[0] - MODULUS; + } - public static Scalar operator *(in Scalar a, in Scalar b) - { - ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); - ulong r0, r1, r2, r3, r4, r5, r6, r7; - ulong carry; - - (r0, carry) = Mac(0, self[0], rhs[0], 0); - (r1, carry) = Mac(0, self[0], rhs[1], carry); - (r2, carry) = Mac(0, self[0], rhs[2], carry); - (r3, r4) = Mac(0, self[0], rhs[3], carry); - - (r1, carry) = Mac(r1, self[1], rhs[0], 0); - (r2, carry) = Mac(r2, self[1], rhs[1], carry); - (r3, carry) = Mac(r3, self[1], rhs[2], carry); - (r4, r5) = Mac(r4, self[1], rhs[3], carry); - - (r2, carry) = Mac(r2, self[2], rhs[0], 0); - (r3, carry) = Mac(r3, self[2], rhs[1], carry); - (r4, carry) = Mac(r4, self[2], rhs[2], carry); - (r5, r6) = Mac(r5, self[2], rhs[3], carry); - - (r3, carry) = Mac(r3, self[3], rhs[0], 0); - (r4, carry) = Mac(r4, self[3], rhs[1], carry); - (r5, carry) = Mac(r5, self[3], rhs[2], carry); - (r6, r7) = Mac(r6, self[3], rhs[3], carry); - - return MontgomeryReduce(r0, r1, r2, r3, r4, r5, r6, r7); - } + public static Scalar operator *(in Scalar a, in Scalar b) + { + ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); + ulong r0, r1, r2, r3, r4, r5, r6, r7; + ulong carry; + + (r0, carry) = Mac(0, self[0], rhs[0], 0); + (r1, carry) = Mac(0, self[0], rhs[1], carry); + (r2, carry) = Mac(0, self[0], rhs[2], carry); + (r3, r4) = Mac(0, self[0], rhs[3], carry); + + (r1, carry) = Mac(r1, self[1], rhs[0], 0); + (r2, carry) = Mac(r2, self[1], rhs[1], carry); + (r3, carry) = Mac(r3, self[1], rhs[2], carry); + (r4, r5) = Mac(r4, self[1], rhs[3], carry); + + (r2, carry) = Mac(r2, self[2], rhs[0], 0); + (r3, carry) = Mac(r3, self[2], rhs[1], carry); + (r4, carry) = Mac(r4, self[2], rhs[2], carry); + (r5, r6) = Mac(r5, self[2], rhs[3], carry); + + (r3, carry) = Mac(r3, self[3], rhs[0], 0); + (r4, carry) = Mac(r4, self[3], rhs[1], carry); + (r5, carry) = Mac(r5, self[3], rhs[2], carry); + (r6, r7) = Mac(r6, self[3], rhs[3], carry); + + return MontgomeryReduce(r0, r1, r2, r3, r4, r5, r6, r7); + } - public static Scalar operator -(in Scalar a, in Scalar b) - { - ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); - ulong d0, d1, d2, d3; - ulong carry, borrow; - - (d0, borrow) = Sbb(self[0], rhs[0], 0); - (d1, borrow) = Sbb(self[1], rhs[1], borrow); - (d2, borrow) = Sbb(self[2], rhs[2], borrow); - (d3, borrow) = Sbb(self[3], rhs[3], borrow); - - borrow = borrow == 0 ? ulong.MinValue : ulong.MaxValue; - (d0, carry) = Adc(d0, MODULUS_LIMBS_64[0] & borrow, 0); - (d1, carry) = Adc(d1, MODULUS_LIMBS_64[1] & borrow, carry); - (d2, carry) = Adc(d2, MODULUS_LIMBS_64[2] & borrow, carry); - (d3, _) = Adc(d3, MODULUS_LIMBS_64[3] & borrow, carry); - - ReadOnlySpan tmp = stackalloc[] { d0, d1, d2, d3 }; - return MemoryMarshal.Cast(tmp)[0]; - } + public static Scalar operator -(in Scalar a, in Scalar b) + { + ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); + ulong d0, d1, d2, d3; + ulong carry, borrow; + + (d0, borrow) = Sbb(self[0], rhs[0], 0); + (d1, borrow) = Sbb(self[1], rhs[1], borrow); + (d2, borrow) = Sbb(self[2], rhs[2], borrow); + (d3, borrow) = Sbb(self[3], rhs[3], borrow); + + borrow = borrow == 0 ? ulong.MinValue : ulong.MaxValue; + (d0, carry) = Adc(d0, MODULUS_LIMBS_64[0] & borrow, 0); + (d1, carry) = Adc(d1, MODULUS_LIMBS_64[1] & borrow, carry); + (d2, carry) = Adc(d2, MODULUS_LIMBS_64[2] & borrow, carry); + (d3, _) = Adc(d3, MODULUS_LIMBS_64[3] & borrow, carry); + + ReadOnlySpan tmp = stackalloc[] { d0, d1, d2, d3 }; + return MemoryMarshal.Cast(tmp)[0]; + } - public static Scalar operator +(in Scalar a, in Scalar b) - { - ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); - ulong d0, d1, d2, d3; - ulong carry; - - (d0, carry) = Adc(self[0], rhs[0], 0); - (d1, carry) = Adc(self[1], rhs[1], carry); - (d2, carry) = Adc(self[2], rhs[2], carry); - (d3, _) = Adc(self[3], rhs[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - ReadOnlySpan tmp = stackalloc[] { d0, d1, d2, d3 }; - return MemoryMarshal.Cast(tmp)[0] - MODULUS; - } + public static Scalar operator +(in Scalar a, in Scalar b) + { + ReadOnlySpan self = a.GetSpanU64(), rhs = b.GetSpanU64(); + ulong d0, d1, d2, d3; + ulong carry; + + (d0, carry) = Adc(self[0], rhs[0], 0); + (d1, carry) = Adc(self[1], rhs[1], carry); + (d2, carry) = Adc(self[2], rhs[2], carry); + (d3, _) = Adc(self[3], rhs[3], carry); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + ReadOnlySpan tmp = stackalloc[] { d0, d1, d2, d3 }; + return MemoryMarshal.Cast(tmp)[0] - MODULUS; + } - public static Scalar operator -(in Scalar a) - { - ReadOnlySpan self = a.GetSpanU64(); - ulong d0, d1, d2, d3; - ulong borrow; - - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - (d0, borrow) = Sbb(MODULUS_LIMBS_64[0], self[0], 0); - (d1, borrow) = Sbb(MODULUS_LIMBS_64[1], self[1], borrow); - (d2, borrow) = Sbb(MODULUS_LIMBS_64[2], self[2], borrow); - (d3, _) = Sbb(MODULUS_LIMBS_64[3], self[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - ulong mask = a.IsZero ? ulong.MinValue : ulong.MaxValue; - - ReadOnlySpan tmp = stackalloc[] { d0 & mask, d1 & mask, d2 & mask, d3 & mask }; - return MemoryMarshal.Cast(tmp)[0]; - } + public static Scalar operator -(in Scalar a) + { + ReadOnlySpan self = a.GetSpanU64(); + ulong d0, d1, d2, d3; + ulong borrow; + + // Subtract `self` from `MODULUS` to negate. Ignore the final + // borrow because it cannot underflow; self is guaranteed to + // be in the field. + (d0, borrow) = Sbb(MODULUS_LIMBS_64[0], self[0], 0); + (d1, borrow) = Sbb(MODULUS_LIMBS_64[1], self[1], borrow); + (d2, borrow) = Sbb(MODULUS_LIMBS_64[2], self[2], borrow); + (d3, _) = Sbb(MODULUS_LIMBS_64[3], self[3], borrow); + + // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is + // zero if `self` was zero, and `u64::max_value()` if self was nonzero. + ulong mask = a.IsZero ? ulong.MinValue : ulong.MaxValue; + + ReadOnlySpan tmp = stackalloc[] { d0 & mask, d1 & mask, d2 & mask, d3 & mask }; + return MemoryMarshal.Cast(tmp)[0]; + } - #region Instance math methods + #region Instance math methods - public Scalar Negate() => -this; - public Scalar Multiply(in Scalar value) => this * value; - public Scalar Sum(in Scalar value) => this + value; - public Scalar Subtract(in Scalar value) => this - value; + public Scalar Negate() => -this; + public Scalar Multiply(in Scalar value) => this * value; + public Scalar Sum(in Scalar value) => this + value; + public Scalar Subtract(in Scalar value) => this - value; - #endregion + #endregion + } } diff --git a/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs b/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs index 56bab5b067..4c33f30fbd 100644 --- a/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs +++ b/src/Neo.Cryptography.BLS12_381/ScalarConstants.cs @@ -9,88 +9,89 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381; - -static class ScalarConstants +namespace Neo.Cryptography.BLS12_381 { - // The modulus as u32 limbs. - public static readonly uint[] MODULUS_LIMBS_32 = + static class ScalarConstants { - 0x0000_0001, - 0xffff_ffff, - 0xfffe_5bfe, - 0x53bd_a402, - 0x09a1_d805, - 0x3339_d808, - 0x299d_7d48, - 0x73ed_a753 - }; + // The modulus as u32 limbs. + public static readonly uint[] MODULUS_LIMBS_32 = + { + 0x0000_0001, + 0xffff_ffff, + 0xfffe_5bfe, + 0x53bd_a402, + 0x09a1_d805, + 0x3339_d808, + 0x299d_7d48, + 0x73ed_a753 + }; - // The modulus as u64 limbs. - public static readonly ulong[] MODULUS_LIMBS_64 = - { - 0xffff_ffff_0000_0001, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48 - }; + // The modulus as u64 limbs. + public static readonly ulong[] MODULUS_LIMBS_64 = + { + 0xffff_ffff_0000_0001, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }; - // q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 - public static readonly Scalar MODULUS = new(MODULUS_LIMBS_64); + // q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 + public static readonly Scalar MODULUS = new(MODULUS_LIMBS_64); - // The number of bits needed to represent the modulus. - public const uint MODULUS_BITS = 255; + // The number of bits needed to represent the modulus. + public const uint MODULUS_BITS = 255; - // GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) - public static readonly Scalar GENERATOR = new(new ulong[] - { - 0x0000_000e_ffff_fff1, - 0x17e3_63d3_0018_9c0f, - 0xff9c_5787_6f84_57b0, - 0x3513_3220_8fc5_a8c4 - }); + // GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) + public static readonly Scalar GENERATOR = new(new ulong[] + { + 0x0000_000e_ffff_fff1, + 0x17e3_63d3_0018_9c0f, + 0xff9c_5787_6f84_57b0, + 0x3513_3220_8fc5_a8c4 + }); - // INV = -(q^{-1} mod 2^64) mod 2^64 - public const ulong INV = 0xffff_fffe_ffff_ffff; + // INV = -(q^{-1} mod 2^64) mod 2^64 + public const ulong INV = 0xffff_fffe_ffff_ffff; - // R = 2^256 mod q - public static readonly Scalar R = new(new ulong[] - { - 0x0000_0001_ffff_fffe, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f - }); + // R = 2^256 mod q + public static readonly Scalar R = new(new ulong[] + { + 0x0000_0001_ffff_fffe, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f + }); - // R^2 = 2^512 mod q - public static readonly Scalar R2 = new(new ulong[] - { - 0xc999_e990_f3f2_9c6d, - 0x2b6c_edcb_8792_5c23, - 0x05d3_1496_7254_398f, - 0x0748_d9d9_9f59_ff11 - }); + // R^2 = 2^512 mod q + public static readonly Scalar R2 = new(new ulong[] + { + 0xc999_e990_f3f2_9c6d, + 0x2b6c_edcb_8792_5c23, + 0x05d3_1496_7254_398f, + 0x0748_d9d9_9f59_ff11 + }); - // R^3 = 2^768 mod q - public static readonly Scalar R3 = new(new ulong[] - { - 0xc62c_1807_439b_73af, - 0x1b3e_0d18_8cf0_6990, - 0x73d1_3c71_c7b5_f418, - 0x6e2a_5bb9_c8db_33e9 - }); + // R^3 = 2^768 mod q + public static readonly Scalar R3 = new(new ulong[] + { + 0xc62c_1807_439b_73af, + 0x1b3e_0d18_8cf0_6990, + 0x73d1_3c71_c7b5_f418, + 0x6e2a_5bb9_c8db_33e9 + }); - // 2^S * t = MODULUS - 1 with t odd - public const uint S = 32; + // 2^S * t = MODULUS - 1 with t odd + public const uint S = 32; - // GENERATOR^t where t * 2^s + 1 = q with t odd. - // In other words, this is a 2^s root of unity. - // `GENERATOR = 7 mod q` is a generator of the q - 1 order multiplicative subgroup. - public static readonly Scalar ROOT_OF_UNITY = new(new ulong[] - { - 0xb9b5_8d8c_5f0e_466a, - 0x5b1b_4c80_1819_d7ec, - 0x0af5_3ae3_52a3_1e64, - 0x5bf3_adda_19e9_b27b - }); + // GENERATOR^t where t * 2^s + 1 = q with t odd. + // In other words, this is a 2^s root of unity. + // `GENERATOR = 7 mod q` is a generator of the q - 1 order multiplicative subgroup. + public static readonly Scalar ROOT_OF_UNITY = new(new ulong[] + { + 0xb9b5_8d8c_5f0e_466a, + 0x5b1b_4c80_1819_d7ec, + 0x0af5_3ae3_52a3_1e64, + 0x5bf3_adda_19e9_b27b + }); + } } diff --git a/src/Neo.Extensions/ByteArrayEqualityComparer.cs b/src/Neo.Extensions/ByteArrayEqualityComparer.cs new file mode 100644 index 0000000000..789e58758e --- /dev/null +++ b/src/Neo.Extensions/ByteArrayEqualityComparer.cs @@ -0,0 +1,34 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ByteArrayEqualityComparer.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 System; +using System.Collections.Generic; + +namespace Neo.Extensions +{ + public class ByteArrayEqualityComparer : IEqualityComparer + { + public static readonly ByteArrayEqualityComparer Default = new(); + + public bool Equals(byte[]? x, byte[]? y) + { + if (ReferenceEquals(x, y)) return true; + if (x is null || y is null || x.Length != y.Length) return false; + + return x.AsSpan().SequenceEqual(y.AsSpan()); + } + + public int GetHashCode(byte[] obj) + { + return obj.XxHash3_32(); + } + } +} diff --git a/src/Neo.Extensions/ByteExtensions.cs b/src/Neo.Extensions/ByteExtensions.cs index 013a8ef1cc..702b71f109 100644 --- a/src/Neo.Extensions/ByteExtensions.cs +++ b/src/Neo.Extensions/ByteExtensions.cs @@ -10,23 +10,66 @@ // modifications are permitted. using System; +using System.IO.Hashing; +using System.Runtime.CompilerServices; using System.Text; namespace Neo.Extensions { public static class ByteExtensions { + private const int DefaultXxHash3Seed = 40343; + private const string s_hexChars = "0123456789abcdef"; + + /// + /// Computes the 32-bit hash value for the specified byte array using the xxhash3 algorithm. + /// + /// The input to compute the hash code for. + /// The seed used by the xxhash3 algorithm. + /// The computed hash code. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int XxHash3_32(this ReadOnlySpan value, long seed = DefaultXxHash3Seed) + { + return HashCode.Combine(XxHash3.HashToUInt64(value, seed)); + } + + /// + /// Computes the 32-bit hash value for the specified byte array using the xxhash3 algorithm. + /// + /// The input to compute the hash code for. + /// The seed used by the xxhash3 algorithm. + /// The computed hash code. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int XxHash3_32(this byte[] value, long seed = DefaultXxHash3Seed) + { + return XxHash3_32(value.AsSpan(), seed); + } + /// /// Converts a byte array to hex . /// /// The byte array to convert. /// The converted hex . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToHexString(this byte[] value) { - StringBuilder sb = new(); - foreach (var b in value) - sb.AppendFormat("{0:x2}", b); - return sb.ToString(); +#if NET9_0_OR_GREATER + return Convert.ToHexStringLower(value); +#else + if (value is null) + throw new ArgumentNullException(nameof(value)); + + return string.Create(value.Length * 2, value, (span, bytes) => + { + for (var i = 0; i < bytes.Length; i++) + { + var b = bytes[i]; + span[i * 2] = s_hexChars[b >> 4]; + span[i * 2 + 1] = s_hexChars[b & 0xF]; + } + }); +#endif } /// @@ -35,12 +78,25 @@ public static string ToHexString(this byte[] value) /// The byte array to convert. /// Indicates whether it should be converted in the reversed byte order. /// The converted hex . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToHexString(this byte[] value, bool reverse = false) { - StringBuilder sb = new(); - for (var i = 0; i < value.Length; i++) - sb.AppendFormat("{0:x2}", value[reverse ? value.Length - i - 1 : i]); - return sb.ToString(); + if (!reverse) + return ToHexString(value); + + if (value is null) + throw new ArgumentNullException(nameof(value)); + + return string.Create(value.Length * 2, value, (span, bytes) => + { + for (var i = 0; i < bytes.Length; i++) + { + var b = bytes[bytes.Length - i - 1]; + span[i * 2] = s_hexChars[b >> 4]; + span[i * 2 + 1] = s_hexChars[b & 0xF]; + } + }); } /// @@ -48,12 +104,22 @@ public static string ToHexString(this byte[] value, bool reverse = false) /// /// The byte array to convert. /// The converted hex . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToHexString(this ReadOnlySpan value) { - StringBuilder sb = new(); - foreach (var b in value) - sb.AppendFormat("{0:x2}", b); +#if NET9_0_OR_GREATER + return Convert.ToHexStringLower(value); +#else + // string.Create with ReadOnlySpan not supported in NET5 or lower + var sb = new StringBuilder(value.Length * 2); + for (var i = 0; i < value.Length; i++) + { + var b = value[i]; + sb.Append(s_hexChars[b >> 4]); + sb.Append(s_hexChars[b & 0xF]); + } return sb.ToString(); +#endif } } } diff --git a/src/Neo.Extensions/Collections/CollectionExtensions.cs b/src/Neo.Extensions/Collections/CollectionExtensions.cs new file mode 100644 index 0000000000..497bed5511 --- /dev/null +++ b/src/Neo.Extensions/Collections/CollectionExtensions.cs @@ -0,0 +1,82 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// CollectionExtensions.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 System; +using System.Collections.Generic; + + +namespace Neo.Extensions +{ + public static class CollectionExtensions + { + /// + /// Removes the key-value pairs from the dictionary that match the specified predicate. + /// + /// The type of the keys in the dictionary. + /// The type of the values in the dictionary. + /// The dictionary to remove key-value pairs from. + /// The predicate to match key-value pairs. + /// An action to perform after each key-value pair is removed. + public static void RemoveWhere( + this IDictionary dict, + Func, bool> predicate, + Action>? afterRemoved = null) + { + var items = new List>(); + foreach (var item in dict) // avoid linq + { + if (predicate(item)) + items.Add(item); + } + + foreach (var item in items) + { + if (dict.Remove(item.Key)) + afterRemoved?.Invoke(item); + } + } + + /// + /// Chunks the source collection into chunks of the specified size. + /// For example, if the source collection is [1, 2, 3, 4, 5] and the chunk size is 3, the result will be [[1, 2, 3], [4, 5]]. + /// + /// The type of the elements in the collection. + /// The collection to chunk. + /// The size of each chunk. + /// An enumerable of arrays, each containing a chunk of the source collection. + /// Thrown when the source collection is null. + /// Thrown when the chunk size is less than or equal to 0. + public static IEnumerable Chunk(this IReadOnlyCollection source, int chunkSize) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + if (chunkSize <= 0) + throw new ArgumentOutOfRangeException(nameof(chunkSize), "Chunk size must > 0."); + + using IEnumerator enumerator = source.GetEnumerator(); + for (var remain = source.Count; remain > 0;) + { + var chunk = new T[Math.Min(remain, chunkSize)]; + for (var i = 0; i < chunk.Length; i++) + { + if (!enumerator.MoveNext()) // Additional checks + throw new InvalidOperationException("unexpected end of sequence"); + chunk[i] = enumerator.Current; + } + + remain -= chunk.Length; + yield return chunk; + } + } + } +} diff --git a/src/Neo.Extensions/Neo.Extensions.csproj b/src/Neo.Extensions/Neo.Extensions.csproj index f424dd0809..5d66927611 100644 --- a/src/Neo.Extensions/Neo.Extensions.csproj +++ b/src/Neo.Extensions/Neo.Extensions.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0 + netstandard2.1;net9.0 enable true Neo.Extensions @@ -10,7 +10,8 @@ - + + diff --git a/src/Neo.Extensions/UnsafeData.cs b/src/Neo.Extensions/UnsafeData.cs index 6d4c9a6f8e..50126c25f4 100644 --- a/src/Neo.Extensions/UnsafeData.cs +++ b/src/Neo.Extensions/UnsafeData.cs @@ -18,14 +18,16 @@ public static class UnsafeData /// /// The length of the data. /// The size of variable-length of the data. - public static int GetVarSize(int value) + public static byte GetVarSize(long value) { if (value < 0xFD) return sizeof(byte); - else if (value <= 0xFFFF) + else if (value <= ushort.MaxValue) return sizeof(byte) + sizeof(ushort); - else + else if (value <= uint.MaxValue) return sizeof(byte) + sizeof(uint); + else + return sizeof(byte) + sizeof(ulong); } } } diff --git a/src/Neo.GUI/GUI/ChangePasswordDialog.cs b/src/Neo.GUI/GUI/ChangePasswordDialog.cs index f6ea079e41..30f77aa546 100644 --- a/src/Neo.GUI/GUI/ChangePasswordDialog.cs +++ b/src/Neo.GUI/GUI/ChangePasswordDialog.cs @@ -10,12 +10,14 @@ // modifications are permitted. using System; +using System.ComponentModel; using System.Windows.Forms; namespace Neo.GUI { internal partial class ChangePasswordDialog : Form { + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string OldPassword { get @@ -28,6 +30,7 @@ public string OldPassword } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string NewPassword { get diff --git a/src/Neo.GUI/GUI/CreateWalletDialog.cs b/src/Neo.GUI/GUI/CreateWalletDialog.cs index 770cd07cac..08deaf3345 100644 --- a/src/Neo.GUI/GUI/CreateWalletDialog.cs +++ b/src/Neo.GUI/GUI/CreateWalletDialog.cs @@ -10,6 +10,7 @@ // modifications are permitted. using System; +using System.ComponentModel; using System.Windows.Forms; namespace Neo.GUI @@ -21,6 +22,7 @@ public CreateWalletDialog() InitializeComponent(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string Password { get @@ -34,6 +36,7 @@ public string Password } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string WalletPath { get diff --git a/src/Neo.GUI/GUI/ElectionDialog.cs b/src/Neo.GUI/GUI/ElectionDialog.cs index ddc730589e..ef644074d9 100644 --- a/src/Neo.GUI/GUI/ElectionDialog.cs +++ b/src/Neo.GUI/GUI/ElectionDialog.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.IO; +using Neo.Extensions; using Neo.SmartContract.Native; using Neo.VM; using System; diff --git a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs index 9379f45900..e4105001da 100644 --- a/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs +++ b/src/Neo.GUI/GUI/ImportPrivateKeyDialog.cs @@ -10,6 +10,7 @@ // modifications are permitted. using System; +using System.ComponentModel; using System.Windows.Forms; namespace Neo.GUI @@ -21,6 +22,7 @@ public ImportPrivateKeyDialog() InitializeComponent(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string[] WifStrings { get diff --git a/src/Neo.GUI/GUI/OpenWalletDialog.cs b/src/Neo.GUI/GUI/OpenWalletDialog.cs index a43352a9ed..c34495af9f 100644 --- a/src/Neo.GUI/GUI/OpenWalletDialog.cs +++ b/src/Neo.GUI/GUI/OpenWalletDialog.cs @@ -10,6 +10,7 @@ // modifications are permitted. using System; +using System.ComponentModel; using System.Windows.Forms; namespace Neo.GUI @@ -21,6 +22,7 @@ public OpenWalletDialog() InitializeComponent(); } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string Password { get @@ -33,6 +35,7 @@ public string Password } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string WalletPath { get diff --git a/src/Neo.GUI/GUI/TxOutListBox.cs b/src/Neo.GUI/GUI/TxOutListBox.cs index d2e314ecaf..ae6db6c3cf 100644 --- a/src/Neo.GUI/GUI/TxOutListBox.cs +++ b/src/Neo.GUI/GUI/TxOutListBox.cs @@ -23,12 +23,14 @@ internal partial class TxOutListBox : UserControl { public event EventHandler ItemsChanged; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public AssetDescriptor Asset { get; set; } public int ItemCount => listBox1.Items.Count; public IEnumerable Items => listBox1.Items.OfType(); + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool ReadOnly { get @@ -42,6 +44,7 @@ public bool ReadOnly } private UInt160 _script_hash = null; + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public UInt160 ScriptHash { get diff --git a/src/Neo.GUI/GUI/VotingDialog.cs b/src/Neo.GUI/GUI/VotingDialog.cs index d289cc48ca..ff5749e181 100644 --- a/src/Neo.GUI/GUI/VotingDialog.cs +++ b/src/Neo.GUI/GUI/VotingDialog.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.IO; +using Neo.Extensions; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; diff --git a/src/Neo.GUI/Neo.GUI.csproj b/src/Neo.GUI/Neo.GUI.csproj index 28c7d841a5..51997403e8 100644 --- a/src/Neo.GUI/Neo.GUI.csproj +++ b/src/Neo.GUI/Neo.GUI.csproj @@ -4,7 +4,7 @@ 2016-2024 The Neo Project Neo.GUI WinExe - net8.0-windows + net9.0-windows true Neo true diff --git a/src/Neo.IO/ByteArrayEqualityComparer.cs b/src/Neo.IO/ByteArrayEqualityComparer.cs deleted file mode 100644 index 2b6f01491c..0000000000 --- a/src/Neo.IO/ByteArrayEqualityComparer.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// ByteArrayEqualityComparer.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 System.Collections.Generic; - -namespace Neo.IO -{ - internal class ByteArrayEqualityComparer : IEqualityComparer - { - public static readonly ByteArrayEqualityComparer Default = new(); - - public unsafe bool Equals(byte[]? x, byte[]? y) - { - if (ReferenceEquals(x, y)) return true; - if (x is null || y is null) return false; - var len = x.Length; - if (len != y.Length) return false; - if (len == 0) return true; - fixed (byte* xp = x, yp = y) - { - long* xlp = (long*)xp, ylp = (long*)yp; - for (; len >= 8; len -= 8) - { - if (*xlp != *ylp) return false; - xlp++; - ylp++; - } - byte* xbp = (byte*)xlp, ybp = (byte*)ylp; - for (; len > 0; len--) - { - if (*xbp != *ybp) return false; - xbp++; - ybp++; - } - } - return true; - } - - public int GetHashCode(byte[] obj) - { - unchecked - { - var hash = 17; - foreach (var element in obj) - hash = hash * 31 + element; - return hash; - } - } - } -} diff --git a/src/Neo.IO/Caching/KeyedCollectionSlim.cs b/src/Neo.IO/Caching/KeyedCollectionSlim.cs index b825739ddd..9a1f689a37 100644 --- a/src/Neo.IO/Caching/KeyedCollectionSlim.cs +++ b/src/Neo.IO/Caching/KeyedCollectionSlim.cs @@ -13,43 +13,44 @@ using System.Collections; using System.Collections.Generic; -namespace Neo.IO.Caching; - -internal abstract class KeyedCollectionSlim - where TKey : notnull - where TItem : class, IStructuralEquatable, IStructuralComparable, IComparable +namespace Neo.IO.Caching { - private readonly LinkedList _items = new(); - private readonly Dictionary> _dict = []; + internal abstract class KeyedCollectionSlim + where TKey : notnull + where TItem : class, IStructuralEquatable, IStructuralComparable, IComparable + { + private readonly LinkedList _items = new(); + private readonly Dictionary> _dict = []; - public int Count => _dict.Count; - public TItem? First => _items.First?.Value; + public int Count => _dict.Count; + public TItem? First => _items.First?.Value; - protected abstract TKey GetKeyForItem(TItem? item); + protected abstract TKey GetKeyForItem(TItem? item); - public void Add(TItem item) - { - var key = GetKeyForItem(item); - var node = _items.AddLast(item); - if (!_dict.TryAdd(key, node)) + public void Add(TItem item) { - _items.RemoveLast(); - throw new ArgumentException("An element with the same key already exists in the collection."); + var key = GetKeyForItem(item); + var node = _items.AddLast(item); + if (!_dict.TryAdd(key, node)) + { + _items.RemoveLast(); + throw new ArgumentException("An element with the same key already exists in the collection."); + } } - } - public bool Contains(TKey key) => _dict.ContainsKey(key); + public bool Contains(TKey key) => _dict.ContainsKey(key); - public void Remove(TKey key) - { - if (_dict.Remove(key, out var node)) - _items.Remove(node); - } + public void Remove(TKey key) + { + if (_dict.Remove(key, out var node)) + _items.Remove(node); + } - public void RemoveFirst() - { - var key = GetKeyForItem(_items.First?.Value); - _dict.Remove(key); - _items.RemoveFirst(); + public void RemoveFirst() + { + var key = GetKeyForItem(_items.First?.Value); + _dict.Remove(key); + _items.RemoveFirst(); + } } } diff --git a/src/Neo.IO/Neo.IO.csproj b/src/Neo.IO/Neo.IO.csproj index deb98b053b..0b2b858049 100644 --- a/src/Neo.IO/Neo.IO.csproj +++ b/src/Neo.IO/Neo.IO.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0 + netstandard2.1;net9.0 true enable Neo.IO diff --git a/src/Neo.Json/JContainer.cs b/src/Neo.Json/JContainer.cs index 5cc1bc7263..788bc34bc3 100644 --- a/src/Neo.Json/JContainer.cs +++ b/src/Neo.Json/JContainer.cs @@ -9,21 +9,22 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Json; - -public abstract class JContainer : JToken +namespace Neo.Json { - public override JToken? this[int index] => Children[index]; + public abstract class JContainer : JToken + { + public override JToken? this[int index] => Children[index]; - public abstract IReadOnlyList Children { get; } + public abstract IReadOnlyList Children { get; } - public int Count => Children.Count; + public int Count => Children.Count; - public abstract void Clear(); + public abstract void Clear(); - public void CopyTo(JToken?[] array, int arrayIndex) - { - for (int i = 0; i < Count && i + arrayIndex < array.Length; i++) - array[i + arrayIndex] = Children[i]; + public void CopyTo(JToken?[] array, int arrayIndex) + { + for (int i = 0; i < Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = Children[i]; + } } } diff --git a/src/Neo.Json/JNumber.cs b/src/Neo.Json/JNumber.cs index 1ae5d6d0a2..5b142feb41 100644 --- a/src/Neo.Json/JNumber.cs +++ b/src/Neo.Json/JNumber.cs @@ -96,11 +96,12 @@ public override T GetEnum(bool ignoreCase = false) } catch (OverflowException) { - throw new InvalidCastException(); + throw new InvalidCastException($"The value is out of range for the enum {enumType.FullName}"); } + object result = Enum.ToObject(enumType, value); if (!Enum.IsDefined(enumType, result)) - throw new InvalidCastException(); + throw new InvalidCastException($"The value is not defined in the enum {enumType.FullName}"); return (T)result; } diff --git a/src/Neo.Json/JPathToken.cs b/src/Neo.Json/JPathToken.cs index 40054a1fb0..dce574c0e8 100644 --- a/src/Neo.Json/JPathToken.cs +++ b/src/Neo.Json/JPathToken.cs @@ -63,7 +63,7 @@ public static IEnumerable Parse(string expr) i += token.Content.Length - 1; break; default: - throw new FormatException(); + throw new FormatException($"Invalid character '{expr[i]}' at position {i}"); } yield return token; } @@ -78,7 +78,7 @@ private static string ParseString(string expr, int start) end++; if (c == '\'') return expr[start..end]; } - throw new FormatException(); + throw new FormatException("Unterminated string"); } public static string ParseIdentifier(string expr, int start) @@ -112,7 +112,7 @@ private static string ParseNumber(string expr, int start) private static JPathToken DequeueToken(Queue tokens) { if (!tokens.TryDequeue(out JPathToken? token)) - throw new FormatException(); + throw new FormatException("Unexpected end of expression"); return token; } @@ -132,7 +132,7 @@ public static void ProcessJsonPath(ref JToken?[] objects, Queue toke ProcessBracket(ref objects, ref maxDepth, maxObjects, tokens); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } } @@ -152,7 +152,7 @@ private static void ProcessDot(ref JToken?[] objects, ref int maxDepth, int maxO Descent(ref objects, ref maxDepth, maxObjects, token.Content!); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } @@ -162,8 +162,9 @@ private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int switch (token.Type) { case JPathTokenType.Asterisk: - if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) - throw new FormatException(); + var rightBracket = DequeueToken(tokens); + if (rightBracket.Type != JPathTokenType.RightBracket) + throw new FormatException($"Unexpected token {rightBracket.Type}"); Descent(ref objects, ref maxDepth, maxObjects); break; case JPathTokenType.Colon: @@ -183,7 +184,7 @@ private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int Descent(ref objects, ref maxDepth, maxObjects, int.Parse(token.Content!)); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {next.Type}"); } break; case JPathTokenType.String: @@ -197,11 +198,11 @@ private static void ProcessBracket(ref JToken?[] objects, ref int maxDepth, int Descent(ref objects, ref maxDepth, maxObjects, JToken.Parse($"\"{token.Content!.Trim('\'')}\"")!.GetString()); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {next.Type}"); } break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } @@ -209,7 +210,9 @@ private static void ProcessRecursiveDescent(ref JToken?[] objects, ref int maxDe { List results = new(); JPathToken token = DequeueToken(tokens); - if (token.Type != JPathTokenType.Identifier) throw new FormatException(); + if (token.Type != JPathTokenType.Identifier) + throw new FormatException($"Unexpected token {token.Type}"); + while (objects.Length > 0) { results.AddRange(objects.OfType().SelectMany(p => p.Properties).Where(p => p.Key == token.Content).Select(p => p.Value)); @@ -225,15 +228,16 @@ private static void ProcessSlice(ref JToken?[] objects, ref int maxDepth, int ma switch (token.Type) { case JPathTokenType.Number: - if (DequeueToken(tokens).Type != JPathTokenType.RightBracket) - throw new FormatException(); + var next = DequeueToken(tokens); + if (next.Type != JPathTokenType.RightBracket) + throw new FormatException($"Unexpected token {next.Type}"); DescentRange(ref objects, ref maxDepth, maxObjects, start, int.Parse(token.Content!)); break; case JPathTokenType.RightBracket: DescentRange(ref objects, ref maxDepth, maxObjects, start, 0); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type}"); } } @@ -243,14 +247,16 @@ private static void ProcessUnion(ref JToken?[] objects, ref int maxDepth, int ma while (true) { JPathToken token = DequeueToken(tokens); - if (token.Type != first.Type) throw new FormatException(); + if (token.Type != first.Type) + throw new FormatException($"Unexpected token {token.Type} != {first.Type}"); items.Add(token); token = DequeueToken(tokens); if (token.Type == JPathTokenType.RightBracket) break; if (token.Type != JPathTokenType.Comma) - throw new FormatException(); + throw new FormatException($"Unexpected token {token.Type} != {JPathTokenType.Comma}"); } + switch (first.Type) { case JPathTokenType.Number: @@ -260,16 +266,19 @@ private static void ProcessUnion(ref JToken?[] objects, ref int maxDepth, int ma Descent(ref objects, ref maxDepth, maxObjects, items.Select(p => JToken.Parse($"\"{p.Content!.Trim('\'')}\"")!.GetString()).ToArray()); break; default: - throw new FormatException(); + throw new FormatException($"Unexpected token {first.Type}"); } } private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects) { - if (maxDepth <= 0) throw new InvalidOperationException(); + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => p.Children).ToArray(); - if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); + if (objects.Length > maxObjects) + throw new InvalidOperationException(nameof(maxObjects)); } private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params string[] names) @@ -280,10 +289,14 @@ private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObje if (obj.ContainsProperty(name)) yield return obj[name]; } - if (maxDepth <= 0) throw new InvalidOperationException(); + + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => GetProperties(p, names)).ToArray(); - if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); + if (objects.Length > maxObjects) + throw new InvalidOperationException(nameof(maxObjects)); } private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObjects, params int[] indexes) @@ -297,16 +310,22 @@ private static void Descent(ref JToken?[] objects, ref int maxDepth, int maxObje yield return array[i]; } } - if (maxDepth <= 0) throw new InvalidOperationException(); + + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => GetElements(p, indexes)).ToArray(); - if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); + if (objects.Length > maxObjects) + throw new InvalidOperationException(nameof(maxObjects)); } private static void DescentRange(ref JToken?[] objects, ref int maxDepth, int maxObjects, int start, int end) { - if (maxDepth <= 0) throw new InvalidOperationException(); + if (maxDepth <= 0) + throw new InvalidOperationException("Exceeded max depth"); --maxDepth; + objects = objects.OfType().SelectMany(p => { int iStart = start >= 0 ? start : start + p.Count; @@ -315,6 +334,7 @@ private static void DescentRange(ref JToken?[] objects, ref int maxDepth, int ma int count = iEnd - iStart; return p.Skip(iStart).Take(count); }).ToArray(); + if (objects.Length > maxObjects) throw new InvalidOperationException(nameof(maxObjects)); } } diff --git a/src/Neo.Json/JPathTokenType.cs b/src/Neo.Json/JPathTokenType.cs index ea25a3609d..aa26879c2b 100644 --- a/src/Neo.Json/JPathTokenType.cs +++ b/src/Neo.Json/JPathTokenType.cs @@ -9,18 +9,19 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Json; - -enum JPathTokenType : byte +namespace Neo.Json { - Root, - Dot, - LeftBracket, - RightBracket, - Asterisk, - Comma, - Colon, - Identifier, - String, - Number + enum JPathTokenType : byte + { + Root, + Dot, + LeftBracket, + RightBracket, + Asterisk, + Comma, + Colon, + Identifier, + String, + Number + } } diff --git a/src/Neo.Json/JToken.cs b/src/Neo.Json/JToken.cs index bb6a510a69..a01beba974 100644 --- a/src/Neo.Json/JToken.cs +++ b/src/Neo.Json/JToken.cs @@ -12,294 +12,300 @@ using System.Text.Json; using static Neo.Json.Utility; -namespace Neo.Json; - -/// -/// Represents an abstract JSON token. -/// -public abstract class JToken +namespace Neo.Json { /// - /// Represents a token. - /// - public const JToken? Null = null; - - /// - /// Gets or sets the child token at the specified index. + /// Represents an abstract JSON token. /// - /// The zero-based index of the child token to get or set. - /// The child token at the specified index. - public virtual JToken? this[int index] + public abstract class JToken { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } + /// + /// Represents a token. + /// + public const JToken? Null = null; - /// - /// Gets or sets the properties of the JSON object. - /// - /// The key of the property to get or set. - /// The property with the specified name. - public virtual JToken? this[string key] - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } + /// + /// Gets or sets the child token at the specified index. + /// + /// The zero-based index of the child token to get or set. + /// The child token at the specified index. + public virtual JToken? this[int index] + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - /// - /// Converts the current JSON token to a boolean value. - /// - /// The converted value. - public virtual bool AsBoolean() - { - return true; - } + /// + /// Gets or sets the properties of the JSON object. + /// + /// The key of the property to get or set. + /// The property with the specified name. + public virtual JToken? this[string key] + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - /// - /// Converts the current JSON token to an . - /// - /// The type of the . - /// If the current JSON token cannot be converted to type , then the default value is returned. - /// Indicates whether case should be ignored during conversion. - /// The converted value. - public virtual T AsEnum(T defaultValue = default, bool ignoreCase = false) where T : unmanaged, Enum - { - return defaultValue; - } + /// + /// Converts the current JSON token to a boolean value. + /// + /// The converted value. + public virtual bool AsBoolean() + { + return true; + } - /// - /// Converts the current JSON token to a floating point number. - /// - /// The converted value. - public virtual double AsNumber() - { - return double.NaN; - } + /// + /// Converts the current JSON token to an . + /// + /// The type of the . + /// If the current JSON token cannot be converted to type , then the default value is returned. + /// Indicates whether case should be ignored during conversion. + /// The converted value. + public virtual T AsEnum(T defaultValue = default, bool ignoreCase = false) where T : unmanaged, Enum + { + return defaultValue; + } - /// - /// Converts the current JSON token to a . - /// - /// The converted value. - public virtual string AsString() - { - return ToString(); - } + /// + /// Converts the current JSON token to a floating point number. + /// + /// The converted value. + public virtual double AsNumber() + { + return double.NaN; + } - /// - /// Converts the current JSON token to a boolean value. - /// - /// The converted value. - /// The JSON token is not a . - public virtual bool GetBoolean() => throw new InvalidCastException(); + /// + /// Converts the current JSON token to a . + /// + /// The converted value. + public virtual string AsString() + { + return ToString(); + } - public virtual T GetEnum(bool ignoreCase = false) where T : unmanaged, Enum => throw new InvalidCastException(); + /// + /// Converts the current JSON token to a boolean value. + /// + /// The converted value. + /// The JSON token is not a . + public virtual bool GetBoolean() => throw new InvalidCastException(); - /// - /// Converts the current JSON token to a 32-bit signed integer. - /// - /// The converted value. - /// The JSON token is not a . - /// The JSON token cannot be converted to an integer. - /// The JSON token cannot be converted to a 32-bit signed integer. - public int GetInt32() - { - double d = GetNumber(); - if (d % 1 != 0) throw new InvalidCastException(); - return checked((int)d); - } + public virtual T GetEnum(bool ignoreCase = false) where T : unmanaged, Enum => throw new InvalidCastException(); - /// - /// Converts the current JSON token to a floating point number. - /// - /// The converted value. - /// The JSON token is not a . - public virtual double GetNumber() => throw new InvalidCastException(); + /// + /// Converts the current JSON token to a 32-bit signed integer. + /// + /// The converted value. + /// The JSON token is not a . + /// The JSON token cannot be converted to an integer. + /// The JSON token cannot be converted to a 32-bit signed integer. + public int GetInt32() + { + double d = GetNumber(); + if (d % 1 != 0) throw new InvalidCastException(); + return checked((int)d); + } - /// - /// Converts the current JSON token to a . - /// - /// The converted value. - /// The JSON token is not a . - public virtual string GetString() => throw new InvalidCastException(); + /// + /// Converts the current JSON token to a floating point number. + /// + /// The converted value. + /// The JSON token is not a . + public virtual double GetNumber() => throw new InvalidCastException(); - /// - /// Parses a JSON token from a byte array. - /// - /// The byte array that contains the JSON token. - /// The maximum nesting depth when parsing the JSON token. - /// The parsed JSON token. - public static JToken? Parse(ReadOnlySpan value, int max_nest = 64) - { - Utf8JsonReader reader = new(value, new JsonReaderOptions - { - AllowTrailingCommas = false, - CommentHandling = JsonCommentHandling.Skip, - MaxDepth = max_nest - }); - try + /// + /// Converts the current JSON token to a . + /// + /// The converted value. + /// The JSON token is not a . + public virtual string GetString() => throw new InvalidCastException(); + + /// + /// Parses a JSON token from a byte array. + /// + /// The byte array that contains the JSON token. + /// The maximum nesting depth when parsing the JSON token. + /// The parsed JSON token. + public static JToken? Parse(ReadOnlySpan value, int max_nest = 64) { - JToken? json = Read(ref reader); - if (reader.Read()) throw new FormatException(); - return json; + Utf8JsonReader reader = new(value, new JsonReaderOptions + { + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Skip, + MaxDepth = max_nest + }); + try + { + JToken? json = Read(ref reader); + if (reader.Read()) throw new FormatException(); + return json; + } + catch (JsonException ex) + { + throw new FormatException(ex.Message, ex); + } } - catch (JsonException ex) + + /// + /// Parses a JSON token from a . + /// + /// The that contains the JSON token. + /// The maximum nesting depth when parsing the JSON token. + /// The parsed JSON token. + public static JToken? Parse(string value, int max_nest = 64) { - throw new FormatException(ex.Message, ex); + return Parse(StrictUTF8.GetBytes(value), max_nest); } - } - /// - /// Parses a JSON token from a . - /// - /// The that contains the JSON token. - /// The maximum nesting depth when parsing the JSON token. - /// The parsed JSON token. - public static JToken? Parse(string value, int max_nest = 64) - { - return Parse(StrictUTF8.GetBytes(value), max_nest); - } + private static JToken? Read(ref Utf8JsonReader reader, bool skipReading = false) + { + if (!skipReading && !reader.Read()) throw new FormatException(); + return reader.TokenType switch + { + JsonTokenType.False => false, + JsonTokenType.Null => Null, + JsonTokenType.Number => reader.GetDouble(), + JsonTokenType.StartArray => ReadArray(ref reader), + JsonTokenType.StartObject => ReadObject(ref reader), + JsonTokenType.String => ReadString(ref reader), + JsonTokenType.True => true, + _ => throw new FormatException($"Unexpected token {reader.TokenType}"), + }; + } - private static JToken? Read(ref Utf8JsonReader reader, bool skipReading = false) - { - if (!skipReading && !reader.Read()) throw new FormatException(); - return reader.TokenType switch + private static JArray ReadArray(ref Utf8JsonReader reader) { - JsonTokenType.False => false, - JsonTokenType.Null => Null, - JsonTokenType.Number => reader.GetDouble(), - JsonTokenType.StartArray => ReadArray(ref reader), - JsonTokenType.StartObject => ReadObject(ref reader), - JsonTokenType.String => ReadString(ref reader), - JsonTokenType.True => true, - _ => throw new FormatException(), - }; - } + JArray array = new(); + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.EndArray: + return array; + default: + array.Add(Read(ref reader, skipReading: true)); + break; + } + } + throw new FormatException("Unterminated array"); + } - private static JArray ReadArray(ref Utf8JsonReader reader) - { - JArray array = new(); - while (reader.Read()) + private static JObject ReadObject(ref Utf8JsonReader reader) { - switch (reader.TokenType) + JObject obj = new(); + while (reader.Read()) { - case JsonTokenType.EndArray: - return array; - default: - array.Add(Read(ref reader, skipReading: true)); - break; + switch (reader.TokenType) + { + case JsonTokenType.EndObject: + return obj; + case JsonTokenType.PropertyName: + string name = ReadString(ref reader); + if (obj.Properties.ContainsKey(name)) + throw new FormatException($"Duplicate property name: {name}"); + + JToken? value = Read(ref reader); + obj.Properties.Add(name, value); + break; + default: + throw new FormatException($"Unexpected token {reader.TokenType}"); + } } + throw new FormatException("Unterminated object"); } - throw new FormatException(); - } - private static JObject ReadObject(ref Utf8JsonReader reader) - { - JObject obj = new(); - while (reader.Read()) + private static string ReadString(ref Utf8JsonReader reader) { - switch (reader.TokenType) + try + { + return reader.GetString()!; + } + catch (InvalidOperationException ex) { - case JsonTokenType.EndObject: - return obj; - case JsonTokenType.PropertyName: - string name = ReadString(ref reader); - if (obj.Properties.ContainsKey(name)) throw new FormatException(); - JToken? value = Read(ref reader); - obj.Properties.Add(name, value); - break; - default: - throw new FormatException(); + throw new FormatException(ex.Message, ex); } } - throw new FormatException(); - } - private static string ReadString(ref Utf8JsonReader reader) - { - try + /// + /// Encode the current JSON token into a byte array. + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public byte[] ToByteArray(bool indented) { - return reader.GetString()!; + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms, new JsonWriterOptions + { + Indented = indented, + SkipValidation = true + }); + Write(writer); + writer.Flush(); + return ms.ToArray(); } - catch (InvalidOperationException ex) + + /// + /// Encode the current JSON token into a . + /// + /// The encoded JSON token. + public override string ToString() { - throw new FormatException(ex.Message, ex); + return ToString(false); } - } - /// - /// Encode the current JSON token into a byte array. - /// - /// Indicates whether indentation is required. - /// The encoded JSON token. - public byte[] ToByteArray(bool indented) - { - using MemoryStream ms = new(); - using Utf8JsonWriter writer = new(ms, new JsonWriterOptions + /// + /// Encode the current JSON token into a . + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public string ToString(bool indented) { - Indented = indented, - SkipValidation = true - }); - Write(writer); - writer.Flush(); - return ms.ToArray(); - } + return StrictUTF8.GetString(ToByteArray(indented)); + } - /// - /// Encode the current JSON token into a . - /// - /// The encoded JSON token. - public override string ToString() - { - return ToString(false); - } + internal abstract void Write(Utf8JsonWriter writer); - /// - /// Encode the current JSON token into a . - /// - /// Indicates whether indentation is required. - /// The encoded JSON token. - public string ToString(bool indented) - { - return StrictUTF8.GetString(ToByteArray(indented)); - } + public abstract JToken Clone(); - internal abstract void Write(Utf8JsonWriter writer); + public JArray JsonPath(string expr) + { + JToken?[] objects = { this }; + if (expr.Length == 0) return objects; - public abstract JToken Clone(); + Queue tokens = new(JPathToken.Parse(expr)); + JPathToken first = tokens.Dequeue(); + if (first.Type != JPathTokenType.Root) + throw new FormatException($"Unexpected token {first.Type}"); - public JArray JsonPath(string expr) - { - JToken?[] objects = { this }; - if (expr.Length == 0) return objects; - Queue tokens = new(JPathToken.Parse(expr)); - JPathToken first = tokens.Dequeue(); - if (first.Type != JPathTokenType.Root) throw new FormatException(); - JPathToken.ProcessJsonPath(ref objects, tokens); - return objects; - } + JPathToken.ProcessJsonPath(ref objects, tokens); + return objects; + } - public static implicit operator JToken(Enum value) - { - return (JString)value; - } + public static implicit operator JToken(Enum value) + { + return (JString)value; + } - public static implicit operator JToken(JToken?[] value) - { - return (JArray)value; - } + public static implicit operator JToken(JToken?[] value) + { + return (JArray)value; + } - public static implicit operator JToken(bool value) - { - return (JBoolean)value; - } + public static implicit operator JToken(bool value) + { + return (JBoolean)value; + } - public static implicit operator JToken(double value) - { - return (JNumber)value; - } + public static implicit operator JToken(double value) + { + return (JNumber)value; + } - public static implicit operator JToken?(string? value) - { - return (JString?)value; + public static implicit operator JToken?(string? value) + { + return (JString?)value; + } } } diff --git a/src/Neo.Json/Neo.Json.csproj b/src/Neo.Json/Neo.Json.csproj index 0a490a1934..3f0a78b02e 100644 --- a/src/Neo.Json/Neo.Json.csproj +++ b/src/Neo.Json/Neo.Json.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0 + netstandard2.1;net9.0 enable enable Neo.Json @@ -10,7 +10,7 @@ - + diff --git a/src/Neo.Json/OrderedDictionary.KeyCollection.cs b/src/Neo.Json/OrderedDictionary.KeyCollection.cs index 13fbd62e85..8a53bc20da 100644 --- a/src/Neo.Json/OrderedDictionary.KeyCollection.cs +++ b/src/Neo.Json/OrderedDictionary.KeyCollection.cs @@ -11,41 +11,42 @@ using System.Collections; -namespace Neo.Json; - -partial class OrderedDictionary +namespace Neo.Json { - class KeyCollection : ICollection, IReadOnlyList + partial class OrderedDictionary { - private readonly InternalCollection internalCollection; - - public KeyCollection(InternalCollection internalCollection) + class KeyCollection : ICollection, IReadOnlyList { - this.internalCollection = internalCollection; - } + private readonly InternalCollection internalCollection; - public TKey this[int index] => internalCollection[index].Key; + public KeyCollection(InternalCollection internalCollection) + { + this.internalCollection = internalCollection; + } - public int Count => internalCollection.Count; + public TKey this[int index] => internalCollection[index].Key; - public bool IsReadOnly => true; + public int Count => internalCollection.Count; - public void Add(TKey item) => throw new NotSupportedException(); + public bool IsReadOnly => true; - public void Clear() => throw new NotSupportedException(); + public void Add(TKey item) => throw new NotSupportedException(); - public bool Contains(TKey item) => internalCollection.Contains(item); + public void Clear() => throw new NotSupportedException(); - public void CopyTo(TKey[] array, int arrayIndex) - { - for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) - array[i + arrayIndex] = internalCollection[i].Key; - } + public bool Contains(TKey item) => internalCollection.Contains(item); + + public void CopyTo(TKey[] array, int arrayIndex) + { + for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = internalCollection[i].Key; + } - public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Key).GetEnumerator(); + public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Key).GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public bool Remove(TKey item) => throw new NotSupportedException(); + public bool Remove(TKey item) => throw new NotSupportedException(); + } } } diff --git a/src/Neo.Json/OrderedDictionary.ValueCollection.cs b/src/Neo.Json/OrderedDictionary.ValueCollection.cs index f7bb0c4359..0bd2f97ef2 100644 --- a/src/Neo.Json/OrderedDictionary.ValueCollection.cs +++ b/src/Neo.Json/OrderedDictionary.ValueCollection.cs @@ -11,41 +11,42 @@ using System.Collections; -namespace Neo.Json; - -partial class OrderedDictionary +namespace Neo.Json { - class ValueCollection : ICollection, IReadOnlyList + partial class OrderedDictionary { - private readonly InternalCollection internalCollection; - - public ValueCollection(InternalCollection internalCollection) + class ValueCollection : ICollection, IReadOnlyList { - this.internalCollection = internalCollection; - } + private readonly InternalCollection internalCollection; - public TValue this[int index] => internalCollection[index].Value; + public ValueCollection(InternalCollection internalCollection) + { + this.internalCollection = internalCollection; + } - public int Count => internalCollection.Count; + public TValue this[int index] => internalCollection[index].Value; - public bool IsReadOnly => true; + public int Count => internalCollection.Count; - public void Add(TValue item) => throw new NotSupportedException(); + public bool IsReadOnly => true; - public void Clear() => throw new NotSupportedException(); + public void Add(TValue item) => throw new NotSupportedException(); - public bool Contains(TValue item) => item is null ? internalCollection.Any(p => p is null) : internalCollection.Any(p => item.Equals(p.Value)); + public void Clear() => throw new NotSupportedException(); - public void CopyTo(TValue[] array, int arrayIndex) - { - for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) - array[i + arrayIndex] = internalCollection[i].Value; - } + public bool Contains(TValue item) => item is null ? internalCollection.Any(p => p is null) : internalCollection.Any(p => item.Equals(p.Value)); + + public void CopyTo(TValue[] array, int arrayIndex) + { + for (int i = 0; i < internalCollection.Count && i + arrayIndex < array.Length; i++) + array[i + arrayIndex] = internalCollection[i].Value; + } - public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Value).GetEnumerator(); + public IEnumerator GetEnumerator() => internalCollection.Select(p => p.Value).GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public bool Remove(TValue item) => throw new NotSupportedException(); + public bool Remove(TValue item) => throw new NotSupportedException(); + } } } diff --git a/src/Neo.Json/Utility.cs b/src/Neo.Json/Utility.cs index 6fff7211ca..63542c3e8a 100644 --- a/src/Neo.Json/Utility.cs +++ b/src/Neo.Json/Utility.cs @@ -11,16 +11,17 @@ using System.Text; -namespace Neo.Json; - -static class Utility +namespace Neo.Json { - public static Encoding StrictUTF8 { get; } - - static Utility() + static class Utility { - StrictUTF8 = (Encoding)Encoding.UTF8.Clone(); - StrictUTF8.DecoderFallback = DecoderFallback.ExceptionFallback; - StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; + public static Encoding StrictUTF8 { get; } + + static Utility() + { + StrictUTF8 = (Encoding)Encoding.UTF8.Clone(); + StrictUTF8.DecoderFallback = DecoderFallback.ExceptionFallback; + StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; + } } } diff --git a/src/Neo.VM/EvaluationStack.cs b/src/Neo.VM/EvaluationStack.cs index 463df8d12a..9c0d436fe9 100644 --- a/src/Neo.VM/EvaluationStack.cs +++ b/src/Neo.VM/EvaluationStack.cs @@ -69,7 +69,7 @@ IEnumerator IEnumerable.GetEnumerator() [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Insert(int index, StackItem item) { - if (index > innerList.Count) throw new InvalidOperationException($"Insert out of bounds: {index}/{innerList.Count}"); + if (index > innerList.Count) throw new InvalidOperationException($"Insert index is out of stack bounds: {index}/{innerList.Count}"); innerList.Insert(innerList.Count - index, item); referenceCounter.AddStackReference(item); } @@ -92,11 +92,11 @@ internal void MoveTo(EvaluationStack stack, int count = -1) [MethodImpl(MethodImplOptions.AggressiveInlining)] public StackItem Peek(int index = 0) { - if (index >= innerList.Count) throw new InvalidOperationException($"Peek out of bounds: {index}/{innerList.Count}"); + if (index >= innerList.Count) throw new InvalidOperationException($"Peek index is out of stack bounds: {index}/{innerList.Count}"); if (index < 0) { index += innerList.Count; - if (index < 0) throw new InvalidOperationException($"Peek out of bounds: {index}/{innerList.Count}"); + if (index < 0) throw new InvalidOperationException($"Peek index is out of stack bounds: {index}/{innerList.Count}"); } return innerList[innerList.Count - index - 1]; } diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index 8a3167b4c8..7fa1ac1312 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -291,7 +291,7 @@ protected virtual void PostExecuteInstruction(Instruction instruction) { if (ReferenceCounter.Count < Limits.MaxStackSize) return; if (ReferenceCounter.CheckZeroReferred() > Limits.MaxStackSize) - throw new InvalidOperationException($"MaxStackSize exceed: {ReferenceCounter.Count}"); + throw new InvalidOperationException($"MaxStackSize exceed: {ReferenceCounter.Count}/{Limits.MaxStackSize}"); } /// diff --git a/src/Neo.VM/ExecutionEngineLimits.cs b/src/Neo.VM/ExecutionEngineLimits.cs index 6670572fb4..382dc258e3 100644 --- a/src/Neo.VM/ExecutionEngineLimits.cs +++ b/src/Neo.VM/ExecutionEngineLimits.cs @@ -68,7 +68,7 @@ public void AssertMaxItemSize(int size) { if (size < 0 || size > MaxItemSize) { - throw new InvalidOperationException($"MaxItemSize exceed: {size}"); + throw new InvalidOperationException($"MaxItemSize exceed: {size}/{MaxItemSize}"); } } @@ -81,7 +81,7 @@ public void AssertShift(int shift) { if (shift > MaxShift || shift < 0) { - throw new InvalidOperationException($"Invalid shift value: {shift}"); + throw new InvalidOperationException($"Invalid shift value: {shift}/{MaxShift}"); } } } diff --git a/src/Neo.VM/JumpTable/JumpTable.Compound.cs b/src/Neo.VM/JumpTable/JumpTable.Compound.cs index 2447edfa35..47c3fd11a9 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Compound.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Compound.cs @@ -36,7 +36,7 @@ public virtual void PackMap(ExecutionEngine engine, Instruction instruction) { var size = (int)engine.Pop().GetInteger(); if (size < 0 || size * 2 > engine.CurrentContext!.EvaluationStack.Count) - throw new InvalidOperationException($"The value {size} is out of range."); + throw new InvalidOperationException($"The map size is out of valid range, 2*{size}/[0, {engine.CurrentContext!.EvaluationStack.Count}]."); Map map = new(engine.ReferenceCounter); for (var i = 0; i < size; i++) { @@ -59,7 +59,7 @@ public virtual void PackStruct(ExecutionEngine engine, Instruction instruction) { var size = (int)engine.Pop().GetInteger(); if (size < 0 || size > engine.CurrentContext!.EvaluationStack.Count) - throw new InvalidOperationException($"The value {size} is out of range."); + throw new InvalidOperationException($"The struct size is out of valid range, {size}/[0, {engine.CurrentContext!.EvaluationStack.Count}]."); Struct @struct = new(engine.ReferenceCounter); for (var i = 0; i < size; i++) { @@ -81,7 +81,7 @@ public virtual void Pack(ExecutionEngine engine, Instruction instruction) { var size = (int)engine.Pop().GetInteger(); if (size < 0 || size > engine.CurrentContext!.EvaluationStack.Count) - throw new InvalidOperationException($"The value {size} is out of range."); + throw new InvalidOperationException($"The array size is out of valid range, {size}/[0, {engine.CurrentContext!.EvaluationStack.Count}]."); VMArray array = new(engine.ReferenceCounter); for (var i = 0; i < size; i++) { @@ -151,7 +151,7 @@ public virtual void NewArray(ExecutionEngine engine, Instruction instruction) { var n = (int)engine.Pop().GetInteger(); if (n < 0 || n > engine.Limits.MaxStackSize) - throw new InvalidOperationException($"MaxStackSize exceed: {n}"); + throw new InvalidOperationException($"The array size is out of valid range, {n}/[0, {engine.Limits.MaxStackSize}]."); var nullArray = new StackItem[n]; Array.Fill(nullArray, StackItem.Null); engine.Push(new VMArray(engine.ReferenceCounter, nullArray)); @@ -169,7 +169,7 @@ public virtual void NewArray_T(ExecutionEngine engine, Instruction instruction) { var n = (int)engine.Pop().GetInteger(); if (n < 0 || n > engine.Limits.MaxStackSize) - throw new InvalidOperationException($"MaxStackSize exceed: {n}"); + throw new InvalidOperationException($"The array size is out of valid range, {n}/[0, {engine.Limits.MaxStackSize}]."); var type = (StackItemType)instruction.TokenU8; if (!Enum.IsDefined(typeof(StackItemType), type)) @@ -212,7 +212,7 @@ public virtual void NewStruct(ExecutionEngine engine, Instruction instruction) { var n = (int)engine.Pop().GetInteger(); if (n < 0 || n > engine.Limits.MaxStackSize) - throw new InvalidOperationException($"MaxStackSize exceed: {n}"); + throw new InvalidOperationException($"The struct size is out of valid range, {n}/[0, {engine.Limits.MaxStackSize}]."); var nullArray = new StackItem[n]; Array.Fill(nullArray, StackItem.Null); @@ -281,7 +281,7 @@ public virtual void HasKey(ExecutionEngine engine, Instruction instruction) // TODO: Overflow and underflow checking needs to be done. var index = (int)key.GetInteger(); if (index < 0) - throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); + throw new InvalidOperationException($"The negative index {index} is invalid for OpCode.{instruction.OpCode}."); engine.Push(index < array.Count); break; } @@ -297,7 +297,7 @@ public virtual void HasKey(ExecutionEngine engine, Instruction instruction) // TODO: Overflow and underflow checking needs to be done. var index = (int)key.GetInteger(); if (index < 0) - throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); + throw new InvalidOperationException($"The negative index {index} is invalid for OpCode.{instruction.OpCode}."); engine.Push(index < buffer.Size); break; } @@ -307,7 +307,7 @@ public virtual void HasKey(ExecutionEngine engine, Instruction instruction) // TODO: Overflow and underflow checking needs to be done. var index = (int)key.GetInteger(); if (index < 0) - throw new InvalidOperationException($"The negative value {index} is invalid for OpCode.{instruction.OpCode}."); + throw new InvalidOperationException($"The negative index {index} is invalid for OpCode.{instruction.OpCode}."); engine.Push(index < array.Size); break; } @@ -375,14 +375,14 @@ public virtual void PickItem(ExecutionEngine engine, Instruction instruction) { var index = (int)key.GetInteger(); if (index < 0 || index >= array.Count) - throw new CatchableException($"The value {index} is out of range."); + throw new CatchableException($"The index of {nameof(VMArray)} is out of range, {index}/[0, {array.Count})."); engine.Push(array[index]); break; } case Map map: { if (!map.TryGetValue(key, out var value)) - throw new CatchableException($"Key not found in {nameof(Map)}"); + throw new CatchableException($"Key {key} not found in {nameof(Map)}."); engine.Push(value); break; } @@ -391,7 +391,7 @@ public virtual void PickItem(ExecutionEngine engine, Instruction instruction) var byteArray = primitive.GetSpan(); var index = (int)key.GetInteger(); if (index < 0 || index >= byteArray.Length) - throw new CatchableException($"The value {index} is out of range."); + throw new CatchableException($"The index of {nameof(PrimitiveType)} is out of range, {index}/[0, {byteArray.Length})."); engine.Push((BigInteger)byteArray[index]); break; } @@ -399,7 +399,7 @@ public virtual void PickItem(ExecutionEngine engine, Instruction instruction) { var index = (int)key.GetInteger(); if (index < 0 || index >= buffer.Size) - throw new CatchableException($"The value {index} is out of range."); + throw new CatchableException($"The index of {nameof(Types.Buffer)} is out of range, {index}/[0, {buffer.Size})."); engine.Push((BigInteger)buffer.InnerBuffer.Span[index]); break; } @@ -444,7 +444,7 @@ public virtual void SetItem(ExecutionEngine engine, Instruction instruction) { var index = (int)key.GetInteger(); if (index < 0 || index >= array.Count) - throw new CatchableException($"The value {index} is out of range."); + throw new CatchableException($"The index of {nameof(VMArray)} is out of range, {index}/[0, {array.Count})."); array[index] = value; break; } @@ -457,9 +457,9 @@ public virtual void SetItem(ExecutionEngine engine, Instruction instruction) { var index = (int)key.GetInteger(); if (index < 0 || index >= buffer.Size) - throw new CatchableException($"The value {index} is out of range."); + throw new CatchableException($"The index of {nameof(Types.Buffer)} is out of range, {index}/[0, {buffer.Size})."); if (value is not PrimitiveType p) - throw new InvalidOperationException($"Value must be a primitive type in {instruction.OpCode}"); + throw new InvalidOperationException($"Only primitive type values can be set in {nameof(Types.Buffer)} in {instruction.OpCode}."); var b = (int)p.GetInteger(); if (b < sbyte.MinValue || b > byte.MaxValue) throw new InvalidOperationException($"Overflow in {instruction.OpCode}, {b} is not a byte type."); @@ -514,7 +514,7 @@ public virtual void Remove(ExecutionEngine engine, Instruction instruction) case VMArray array: var index = (int)key.GetInteger(); if (index < 0 || index >= array.Count) - throw new InvalidOperationException($"The value {index} is out of range."); + throw new InvalidOperationException($"The index of {nameof(VMArray)} is out of range, {index}/[0, {array.Count})."); array.RemoveAt(index); break; case Map map: diff --git a/src/Neo.VM/JumpTable/JumpTable.Slot.cs b/src/Neo.VM/JumpTable/JumpTable.Slot.cs index 22214924cf..b1ba54efb5 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Slot.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Slot.cs @@ -659,7 +659,7 @@ public virtual void ExecuteStoreToSlot(ExecutionEngine engine, Slot? slot, int i if (slot is null) throw new InvalidOperationException("Slot has not been initialized."); if (index < 0 || index >= slot.Count) - throw new InvalidOperationException($"Index out of range when storing to slot: {index}"); + throw new InvalidOperationException($"Index out of range when storing to slot: {index}, {index}/[0, {slot.Count})."); slot[index] = engine.Pop(); } @@ -674,7 +674,7 @@ public virtual void ExecuteLoadFromSlot(ExecutionEngine engine, Slot? slot, int if (slot is null) throw new InvalidOperationException("Slot has not been initialized."); if (index < 0 || index >= slot.Count) - throw new InvalidOperationException($"Index out of range when loading from slot: {index}"); + throw new InvalidOperationException($"Index out of range when loading from slot: {index}, {index}/[0, {slot.Count})."); engine.Push(slot[index]); } diff --git a/src/Neo.VM/JumpTable/JumpTable.Splice.cs b/src/Neo.VM/JumpTable/JumpTable.Splice.cs index 82e7f5750c..7192872d81 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Splice.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Splice.cs @@ -46,19 +46,19 @@ public virtual void Memcpy(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The count can not be negative for {nameof(OpCode.MEMCPY)}, count: {count}."); int si = (int)engine.Pop().GetInteger(); if (si < 0) - throw new InvalidOperationException($"The value {si} is out of range."); + throw new InvalidOperationException($"The source index can not be negative for {nameof(OpCode.MEMCPY)}, index: {si}."); ReadOnlySpan src = engine.Pop().GetSpan(); if (checked(si + count) > src.Length) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The source index + count is out of range for {nameof(OpCode.MEMCPY)}, index: {si}, count: {count}, {si}/[0, {src.Length}]."); int di = (int)engine.Pop().GetInteger(); if (di < 0) - throw new InvalidOperationException($"The value {di} is out of range."); + throw new InvalidOperationException($"The destination index can not be negative for {nameof(OpCode.MEMCPY)}, index: {si}."); Types.Buffer dst = engine.Pop(); if (checked(di + count) > dst.Size) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The destination index + count is out of range for {nameof(OpCode.MEMCPY)}, index: {di}, count: {count}, {di}/[0, {dst.Size}]."); // TODO: check if we can optimize the memcpy by using peek instead of dup then pop src.Slice(si, count).CopyTo(dst.InnerBuffer.Span[di..]); dst.InvalidateHashCode(); @@ -96,13 +96,13 @@ public virtual void SubStr(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The count can not be negative for {nameof(OpCode.SUBSTR)}, count: {count}."); int index = (int)engine.Pop().GetInteger(); if (index < 0) - throw new InvalidOperationException($"The value {index} is out of range."); + throw new InvalidOperationException($"The index can not be negative for {nameof(OpCode.SUBSTR)}, index: {index}."); var x = engine.Pop().GetSpan(); if (index + count > x.Length) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The index + count is out of range for {nameof(OpCode.SUBSTR)}, index: {index}, count: {count}, {index + count}/[0, {x.Length}]."); Types.Buffer result = new(count, false); x.Slice(index, count).CopyTo(result.InnerBuffer.Span); engine.Push(result); @@ -120,10 +120,10 @@ public virtual void Left(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The count can not be negative for {nameof(OpCode.LEFT)}, count: {count}."); var x = engine.Pop().GetSpan(); if (count > x.Length) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The count is out of range for {nameof(OpCode.LEFT)}, {count}/[0, {x.Length}]."); Types.Buffer result = new(count, false); x[..count].CopyTo(result.InnerBuffer.Span); engine.Push(result); @@ -141,10 +141,10 @@ public virtual void Right(ExecutionEngine engine, Instruction instruction) { int count = (int)engine.Pop().GetInteger(); if (count < 0) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The count can not be negative for {nameof(OpCode.RIGHT)}, count: {count}."); var x = engine.Pop().GetSpan(); if (count > x.Length) - throw new InvalidOperationException($"The value {count} is out of range."); + throw new InvalidOperationException($"The count is out of range for {nameof(OpCode.RIGHT)}, {count}/[0, {x.Length}]."); Types.Buffer result = new(count, false); x[^count..^0].CopyTo(result.InnerBuffer.Span); engine.Push(result); diff --git a/src/Neo.VM/Neo.VM.csproj b/src/Neo.VM/Neo.VM.csproj index eb7f9e7407..e860e29113 100644 --- a/src/Neo.VM/Neo.VM.csproj +++ b/src/Neo.VM/Neo.VM.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0 + netstandard2.1;net9.0 true enable Neo.VM @@ -15,7 +15,7 @@ - + diff --git a/src/Neo.VM/Script.cs b/src/Neo.VM/Script.cs index 1721b6e104..22ca484511 100644 --- a/src/Neo.VM/Script.cs +++ b/src/Neo.VM/Script.cs @@ -31,14 +31,7 @@ public class Script /// /// The length of the script. /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return _value.Length; - } - } + public int Length { get; } /// /// Gets the at the specified index. @@ -74,6 +67,7 @@ public Script(ReadOnlyMemory script) : this(script, false) public Script(ReadOnlyMemory script, bool strictMode) { _value = script; + Length = _value.Length; if (strictMode) { for (int ip = 0; ip < script.Length; ip += GetInstruction(ip).Size) { } @@ -143,11 +137,12 @@ public Script(ReadOnlyMemory script, bool strictMode) /// The position to get the . /// The at the specified position. /// In strict mode, the was not found at the specified position. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Instruction GetInstruction(int ip) { - if (ip >= Length) throw new ArgumentOutOfRangeException(nameof(ip)); if (!_instructions.TryGetValue(ip, out Instruction? instruction)) { + if (ip >= Length) throw new ArgumentOutOfRangeException(nameof(ip)); if (strictMode) throw new ArgumentException($"ip not found with strict mode", nameof(ip)); instruction = new Instruction(_value, ip); _instructions.Add(ip, instruction); diff --git a/src/Neo.VM/ScriptBuilder.cs b/src/Neo.VM/ScriptBuilder.cs index 5615f6b918..5c56c660b1 100644 --- a/src/Neo.VM/ScriptBuilder.cs +++ b/src/Neo.VM/ScriptBuilder.cs @@ -126,8 +126,6 @@ public ScriptBuilder EmitPush(bool value) /// A reference to this instance after the emit operation has completed. public ScriptBuilder EmitPush(ReadOnlySpan data) { - if (data == null) - throw new ArgumentNullException(nameof(data)); if (data.Length < 0x100) { Emit(OpCode.PUSHDATA1); diff --git a/src/Neo.VM/Types/Array.cs b/src/Neo.VM/Types/Array.cs index 903613c228..dddec7d4b3 100644 --- a/src/Neo.VM/Types/Array.cs +++ b/src/Neo.VM/Types/Array.cs @@ -12,6 +12,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Neo.VM.Types { @@ -29,6 +30,7 @@ public class Array : CompoundType, IReadOnlyList /// The item at the specified index. public StackItem this[int index] { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _array[index]; set { @@ -95,7 +97,7 @@ public Array(IReferenceCounter? referenceCounter, IEnumerable? items /// The item to be added. public void Add(StackItem item) { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (IsReadOnly) throw new InvalidOperationException("The array is readonly, can not add item."); _array.Add(item); if (ReferenceCounter == null) return; @@ -109,7 +111,7 @@ public void Add(StackItem item) public override void Clear() { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (IsReadOnly) throw new InvalidOperationException("The array is readonly, can not clear."); if (ReferenceCounter != null) foreach (StackItem item in _array) ReferenceCounter.RemoveReference(item, this); @@ -150,7 +152,7 @@ public IEnumerator GetEnumerator() /// The index of the item to be removed. public void RemoveAt(int index) { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (IsReadOnly) throw new InvalidOperationException("The array is readonly, can not remove item."); ReferenceCounter?.RemoveReference(_array[index], this); _array.RemoveAt(index); } @@ -160,7 +162,7 @@ public void RemoveAt(int index) /// public void Reverse() { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (IsReadOnly) throw new InvalidOperationException("The array is readonly, can not reverse."); _array.Reverse(); } } diff --git a/src/Neo.VM/Types/Boolean.cs b/src/Neo.VM/Types/Boolean.cs index e0a9864d92..419ca23431 100644 --- a/src/Neo.VM/Types/Boolean.cs +++ b/src/Neo.VM/Types/Boolean.cs @@ -47,6 +47,7 @@ public override bool Equals(StackItem? other) return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool GetBoolean() { return value; @@ -57,6 +58,7 @@ public override int GetHashCode() return HashCode.Combine(value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override BigInteger GetInteger() { return value ? BigInteger.One : BigInteger.Zero; diff --git a/src/Neo.VM/Types/ByteString.cs b/src/Neo.VM/Types/ByteString.cs index 8869092b03..110cf5c17f 100644 --- a/src/Neo.VM/Types/ByteString.cs +++ b/src/Neo.VM/Types/ByteString.cs @@ -60,7 +60,7 @@ internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) internal bool Equals(StackItem? other, ref uint limits) { if (Size > limits || limits == 0) - throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + throw new InvalidOperationException($"The operand exceeds the maximum comparable size, {Size}/{limits}."); uint comparedSize = 1; try { @@ -68,7 +68,7 @@ internal bool Equals(StackItem? other, ref uint limits) comparedSize = Math.Max((uint)Math.Max(Size, b.Size), comparedSize); if (ReferenceEquals(this, b)) return true; if (b.Size > limits) - throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + throw new InvalidOperationException($"The operand exceeds the maximum comparable size, {b.Size}/{limits}."); return Equals(b); } finally @@ -77,15 +77,17 @@ internal bool Equals(StackItem? other, ref uint limits) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool GetBoolean() { if (Size > Integer.MaxSize) throw new InvalidCastException(); - return Unsafe.NotZero(GetSpan()); + return GetSpan().NotZero(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override BigInteger GetInteger() { - if (Size > Integer.MaxSize) throw new InvalidCastException($"MaxSize exceed: {Size}"); + if (Size > Integer.MaxSize) throw new InvalidCastException($"Can not convert {nameof(ByteString)} to an integer, MaxSize of {nameof(Types.Integer)} is exceeded: {Size}/{Integer.MaxSize}."); return new BigInteger(GetSpan()); } diff --git a/src/Neo.VM/Types/Integer.cs b/src/Neo.VM/Types/Integer.cs index 4d997d6c5a..618a170e19 100644 --- a/src/Neo.VM/Types/Integer.cs +++ b/src/Neo.VM/Types/Integer.cs @@ -50,7 +50,7 @@ public Integer(BigInteger value) else { Size = value.GetByteCount(); - if (Size > MaxSize) throw new ArgumentException($"MaxSize exceed: {Size}"); + if (Size > MaxSize) throw new ArgumentException($"Can not create {nameof(Types.Integer)}, MaxSize of {nameof(Types.Integer)} is exceeded: {Size}/{MaxSize}."); } this.value = value; } @@ -62,6 +62,7 @@ public override bool Equals(StackItem? other) return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool GetBoolean() { return !value.IsZero; @@ -72,6 +73,7 @@ public override int GetHashCode() return HashCode.Combine(value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override BigInteger GetInteger() { return value; diff --git a/src/Neo.VM/Types/InteropInterface.cs b/src/Neo.VM/Types/InteropInterface.cs index ee1998316a..320f0ca51b 100644 --- a/src/Neo.VM/Types/InteropInterface.cs +++ b/src/Neo.VM/Types/InteropInterface.cs @@ -53,7 +53,7 @@ public override int GetHashCode() public override T GetInterface() { if (_object is T t) return t; - throw new InvalidCastException($"The item can't be casted to type {typeof(T)}"); + throw new InvalidCastException($"This {nameof(InteropInterface)} can't be casted to type {typeof(T)}."); } internal object GetInterface() diff --git a/src/Neo.VM/Types/Map.cs b/src/Neo.VM/Types/Map.cs index 2986399d59..6e786adbce 100644 --- a/src/Neo.VM/Types/Map.cs +++ b/src/Neo.VM/Types/Map.cs @@ -9,12 +9,12 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.VM.Collections; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.VM.Types { @@ -28,7 +28,7 @@ public class Map : CompoundType, IReadOnlyDictionary /// public const int MaxKeySize = 64; - private readonly OrderedDictionary dictionary = new(); + private readonly Collections.OrderedDictionary dictionary = new(); /// /// Gets or sets the element that has the specified key in the map. @@ -37,17 +37,18 @@ public class Map : CompoundType, IReadOnlyDictionary /// The element that has the specified key in the map. public StackItem this[PrimitiveType key] { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (key.Size > MaxKeySize) - throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + throw new ArgumentException($"Can not get value from map, MaxKeySize of {nameof(Types.Map)} is exceeded: {key.Size}/{MaxKeySize}."); return dictionary[key]; } set { if (key.Size > MaxKeySize) - throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + throw new ArgumentException($"Can not set value to map, MaxKeySize of {nameof(Types.Map)} is exceeded: {key.Size}/{MaxKeySize}."); + if (IsReadOnly) throw new InvalidOperationException("The map is readonly, can not set value."); if (ReferenceCounter != null) { if (dictionary.TryGetValue(key, out StackItem? old_value)) @@ -93,7 +94,7 @@ public Map(IReferenceCounter? referenceCounter = null) public override void Clear() { - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + if (IsReadOnly) throw new InvalidOperationException("The map is readonly, can not clear."); if (ReferenceCounter != null) foreach (var pair in dictionary) { @@ -114,7 +115,7 @@ public override void Clear() public bool ContainsKey(PrimitiveType key) { if (key.Size > MaxKeySize) - throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + throw new ArgumentException($"Can not check if map contains key, MaxKeySize of {nameof(Types.Map)} is exceeded: {key.Size}/{MaxKeySize}."); return dictionary.ContainsKey(key); } @@ -151,8 +152,8 @@ IEnumerator IEnumerable.GetEnumerator() public bool Remove(PrimitiveType key) { if (key.Size > MaxKeySize) - throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); - if (IsReadOnly) throw new InvalidOperationException("The object is readonly."); + throw new ArgumentException($"Can not remove key from map, MaxKeySize of {nameof(Types.Map)} is exceeded: {key.Size}/{MaxKeySize}."); + if (IsReadOnly) throw new InvalidOperationException("The map is readonly, can not remove key."); if (!dictionary.Remove(key, out StackItem? old_value)) return false; ReferenceCounter?.RemoveReference(key, this); @@ -178,7 +179,7 @@ public bool TryGetValue(PrimitiveType key, [MaybeNullWhen(false)] out StackItem #pragma warning restore CS8767 { if (key.Size > MaxKeySize) - throw new ArgumentException($"MaxKeySize exceed: {key.Size}"); + throw new ArgumentException($"Can not get value from map, MaxKeySize of {nameof(Types.Map)} is exceeded: {key.Size}/{MaxKeySize}."); return dictionary.TryGetValue(key, out value); } } diff --git a/src/Neo.VM/Types/Null.cs b/src/Neo.VM/Types/Null.cs index 238d8b2389..6ec860e300 100644 --- a/src/Neo.VM/Types/Null.cs +++ b/src/Neo.VM/Types/Null.cs @@ -11,6 +11,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace Neo.VM.Types { @@ -26,7 +27,7 @@ internal Null() { } public override StackItem ConvertTo(StackItemType type) { if (type == StackItemType.Any || !Enum.IsDefined(typeof(StackItemType), type)) - throw new InvalidCastException($"Type can't be converted to StackItemType: {type}"); + throw new InvalidCastException($"Type {nameof(Null)} can't be converted to StackItemType: {type}"); return this; } @@ -36,11 +37,13 @@ public override bool Equals(StackItem? other) return other is Null; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool GetBoolean() { return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return 0; diff --git a/src/Neo.VM/Types/Struct.cs b/src/Neo.VM/Types/Struct.cs index 344147b5ed..a3f2cd5fe4 100644 --- a/src/Neo.VM/Types/Struct.cs +++ b/src/Neo.VM/Types/Struct.cs @@ -59,7 +59,7 @@ public Struct Clone(ExecutionEngineLimits limits) foreach (StackItem item in b) { count--; - if (count < 0) throw new InvalidOperationException("Beyond clone limits!"); + if (count < 0) throw new InvalidOperationException("Beyond struct subitem clone limits!"); if (item is Struct sb) { Struct sa = new(ReferenceCounter); @@ -100,7 +100,7 @@ internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) while (stack1.Count > 0) { if (count-- == 0) - throw new InvalidOperationException("Too many struct items to compare."); + throw new InvalidOperationException("Too many struct items to compare in struct comparison."); StackItem a = stack1.Pop(); StackItem b = stack2.Pop(); if (a is ByteString byteString) @@ -110,7 +110,7 @@ internal override bool Equals(StackItem? other, ExecutionEngineLimits limits) else { if (maxComparableSize == 0) - throw new InvalidOperationException("The operand exceeds the maximum comparable size."); + throw new InvalidOperationException("The operand exceeds the maximum comparable size in struct comparison."); maxComparableSize -= 1; if (a is Struct sa) { diff --git a/src/Neo.VM/Unsafe.cs b/src/Neo.VM/Unsafe.cs index 86380722b6..fe4c974437 100644 --- a/src/Neo.VM/Unsafe.cs +++ b/src/Neo.VM/Unsafe.cs @@ -19,9 +19,17 @@ unsafe internal static class Unsafe { private const long DefaultXxHash3Seed = 40343; + /// + /// All bytes are zero or not in a byte array + /// + /// The byte array + /// false if all bytes are zero, true otherwise [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool NotZero(ReadOnlySpan x) + public static bool NotZero(this ReadOnlySpan x) { +#if NET7_0_OR_GREATER + return x.IndexOfAnyExcept((byte)0) >= 0; +#else int len = x.Length; if (len == 0) return false; fixed (byte* xp = x) @@ -40,6 +48,7 @@ public static bool NotZero(ReadOnlySpan x) } } return false; +#endif } /// diff --git a/src/Neo.VM/Utility.cs b/src/Neo.VM/Utility.cs index 7c1852b7f9..d562c43f04 100644 --- a/src/Neo.VM/Utility.cs +++ b/src/Neo.VM/Utility.cs @@ -60,7 +60,7 @@ public static BigInteger ModInverse(this BigInteger value, BigInteger modulus) public static BigInteger Sqrt(this BigInteger value) { - if (value < 0) throw new InvalidOperationException("value can not be negative"); + if (value < 0) throw new InvalidOperationException($"value {value} can not be negative for {nameof(Types.Integer)}.{nameof(Sqrt)}."); if (value.IsZero) return BigInteger.Zero; if (value < 4) return BigInteger.One; diff --git a/src/Neo/Cryptography/Helper.cs b/src/Neo/Cryptography/Helper.cs index 761073463b..3a89ecba75 100644 --- a/src/Neo/Cryptography/Helper.cs +++ b/src/Neo/Cryptography/Helper.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Wallets; using Org.BouncyCastle.Crypto.Digests; @@ -18,7 +18,6 @@ using Org.BouncyCastle.Crypto.Parameters; using System; using System.Buffers.Binary; -using System.IO.Hashing; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,7 +31,6 @@ namespace Neo.Cryptography /// public static class Helper { - private const int DefaultXxHash3Seed = 40343; private static readonly bool IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); /// @@ -120,30 +118,6 @@ public static byte[] Sha256(this byte[] value) return sha256.ComputeHash(value); } - /// - /// Computes the 32-bit hash value for the specified byte array using the xxhash3 algorithm. - /// - /// The input to compute the hash code for. - /// The seed used by the xxhash3 algorithm. - /// The computed hash code. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int XxHash3_32(this ReadOnlySpan value, long seed = DefaultXxHash3Seed) - { - return HashCode.Combine(XxHash3.HashToUInt64(value, seed)); - } - - /// - /// Computes the 32-bit hash value for the specified byte array using the xxhash3 algorithm. - /// - /// The input to compute the hash code for. - /// The seed used by the xxhash3 algorithm. - /// The computed hash code. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int XxHash3_32(this byte[] value, long seed = DefaultXxHash3Seed) - { - return XxHash3_32(value.AsSpan(), seed); - } - /// /// Computes the hash value for the specified region of the specified byte array using the sha256 algorithm. /// diff --git a/src/Neo/Cryptography/MerkleTree.cs b/src/Neo/Cryptography/MerkleTree.cs index ed25171ef8..09afa29277 100644 --- a/src/Neo/Cryptography/MerkleTree.cs +++ b/src/Neo/Cryptography/MerkleTree.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using System; using System.Collections; using System.Collections.Generic; diff --git a/src/Neo/Extensions/ByteExtensions.cs b/src/Neo/Extensions/ByteExtensions.cs new file mode 100644 index 0000000000..7bacd33f42 --- /dev/null +++ b/src/Neo/Extensions/ByteExtensions.cs @@ -0,0 +1,66 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ByteExtensions.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.IO; +using System; + +namespace Neo.Extensions +{ + public static class ByteExtensions + { + /// + /// Compresses the specified data using the LZ4 algorithm. + /// + /// The data to be compressed. + /// The compressed data. + public static ReadOnlyMemory CompressLz4(this byte[] data) + { + return data.AsSpan().CompressLz4(); + } + + /// + /// Decompresses the specified data using the LZ4 algorithm. + /// + /// The compressed data. + /// The maximum data size after decompression. + /// The original data. + public static byte[] DecompressLz4(this byte[] data, int maxOutput) + { + return data.AsSpan().DecompressLz4(maxOutput); + } + + /// + /// Converts a byte array to an object. + /// + /// The type to convert to. + /// The byte array to be converted. + /// The offset into the byte array from which to begin using data. + /// The converted object. + public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() + { + MemoryReader reader = new(value.AsMemory(start)); + return reader.ReadSerializable(); + } + + /// + /// Converts a byte array to an array. + /// + /// The type of the array element. + /// The byte array to be converted. + /// The maximum number of elements contained in the converted array. + /// The converted array. + public static T[] AsSerializableArray(this byte[] value, int max = 0x1000000) where T : ISerializable, new() + { + MemoryReader reader = new(value); + return reader.ReadSerializableArray(max); + } + } +} diff --git a/src/Neo/Extensions/Collections/ICollectionExtensions.cs b/src/Neo/Extensions/Collections/ICollectionExtensions.cs index 724def6d80..742446a4e8 100644 --- a/src/Neo/Extensions/Collections/ICollectionExtensions.cs +++ b/src/Neo/Extensions/Collections/ICollectionExtensions.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System.Collections.Generic; using System.IO; diff --git a/src/Neo/Extensions/IO/BinaryReaderExtensions.cs b/src/Neo/Extensions/IO/BinaryReaderExtensions.cs new file mode 100644 index 0000000000..f3448d96e1 --- /dev/null +++ b/src/Neo/Extensions/IO/BinaryReaderExtensions.cs @@ -0,0 +1,79 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BinaryReaderExtensions.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 System; +using System.IO; + +namespace Neo.Extensions +{ + public static class BinaryReaderExtensions + { + /// + /// Reads a byte array of the specified size from a . + /// + /// The for reading data. + /// The size of the byte array. + /// The byte array read from the . + public static byte[] ReadFixedBytes(this BinaryReader reader, int size) + { + var index = 0; + var data = new byte[size]; + + while (size > 0) + { + var bytesRead = reader.Read(data, index, size); + + if (bytesRead <= 0) + { + throw new FormatException(); + } + + size -= bytesRead; + index += bytesRead; + } + + return data; + } + + /// + /// Reads a byte array from a . + /// + /// The for reading data. + /// The maximum size of the byte array. + /// The byte array read from the . + public static byte[] ReadVarBytes(this BinaryReader reader, int max = 0x1000000) + { + return reader.ReadFixedBytes((int)reader.ReadVarInt((ulong)max)); + } + + /// + /// Reads an integer from a . + /// + /// The for reading data. + /// The maximum value of the integer. + /// The integer read from the . + public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxValue) + { + var fb = reader.ReadByte(); + ulong value; + if (fb == 0xFD) + value = reader.ReadUInt16(); + else if (fb == 0xFE) + value = reader.ReadUInt32(); + else if (fb == 0xFF) + value = reader.ReadUInt64(); + else + value = fb; + if (value > max) throw new FormatException(); + return value; + } + } +} diff --git a/src/Neo/Extensions/IO/BinaryWriterExtensions.cs b/src/Neo/Extensions/IO/BinaryWriterExtensions.cs new file mode 100644 index 0000000000..665eae1a69 --- /dev/null +++ b/src/Neo/Extensions/IO/BinaryWriterExtensions.cs @@ -0,0 +1,137 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BinaryWriterExtensions.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.IO; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Neo.Extensions +{ + public static class BinaryWriterExtensions + { + /// + /// Writes an object into a . + /// + /// The for writing data. + /// The object to be written. + public static void Write(this BinaryWriter writer, ISerializable value) + { + value.Serialize(writer); + } + + /// + /// Writes an array into a . + /// + /// The type of the array element. + /// The for writing data. + /// The array to be written. + public static void Write(this BinaryWriter writer, IReadOnlyCollection value) + where T : ISerializable + { + writer.WriteVarInt(value.Count); + foreach (T item in value) + { + item.Serialize(writer); + } + } + + /// + /// Writes a into a . + /// + /// The for writing data. + /// The to be written. + /// The fixed size of the . + public static void WriteFixedString(this BinaryWriter writer, string value, int length) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + if (value.Length > length) + throw new ArgumentException(null, nameof(value)); + var bytes = Utility.StrictUTF8.GetBytes(value); + if (bytes.Length > length) + throw new ArgumentException(null, nameof(value)); + writer.Write(bytes); + if (bytes.Length < length) + writer.Write(stackalloc byte[length - bytes.Length]); + } + + /// + /// Writes an array into a . + /// + /// The type of the array element. + /// The for writing data. + /// The array to be written. + public static void WriteNullableArray(this BinaryWriter writer, T[] value) + where T : class, ISerializable + { + writer.WriteVarInt(value.Length); + foreach (var item in value) + { + var isNull = item is null; + writer.Write(!isNull); + if (isNull) continue; + item.Serialize(writer); + } + } + + /// + /// Writes a byte array into a . + /// + /// The for writing data. + /// The byte array to be written. + public static void WriteVarBytes(this BinaryWriter writer, ReadOnlySpan value) + { + writer.WriteVarInt(value.Length); + writer.Write(value); + } + + /// + /// Writes an integer into a . + /// + /// The for writing data. + /// The integer to be written. + public static void WriteVarInt(this BinaryWriter writer, long value) + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value)); + if (value < 0xFD) + { + writer.Write((byte)value); + } + else if (value <= 0xFFFF) + { + writer.Write((byte)0xFD); + writer.Write((ushort)value); + } + else if (value <= 0xFFFFFFFF) + { + writer.Write((byte)0xFE); + writer.Write((uint)value); + } + else + { + writer.Write((byte)0xFF); + writer.Write(value); + } + } + + /// + /// Writes a into a . + /// + /// The for writing data. + /// The to be written. + public static void WriteVarString(this BinaryWriter writer, string value) + { + writer.WriteVarBytes(Utility.StrictUTF8.GetBytes(value)); + } + } +} diff --git a/src/Neo/Extensions/IO/ISerializableExtensions.cs b/src/Neo/Extensions/IO/ISerializableExtensions.cs new file mode 100644 index 0000000000..67b4f39519 --- /dev/null +++ b/src/Neo/Extensions/IO/ISerializableExtensions.cs @@ -0,0 +1,33 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ISerializableExtensions.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.IO; +using System.IO; + +namespace Neo.Extensions +{ + public static class ISerializableExtensions + { + /// + /// Converts an object to a byte array. + /// + /// The object to be converted. + /// The converted byte array. + public static byte[] ToArray(this ISerializable value) + { + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); + value.Serialize(writer); + writer.Flush(); + return ms.ToArray(); + } + } +} diff --git a/src/Neo/Extensions/IO/MemoryReaderExtensions.cs b/src/Neo/Extensions/IO/MemoryReaderExtensions.cs new file mode 100644 index 0000000000..5591724b80 --- /dev/null +++ b/src/Neo/Extensions/IO/MemoryReaderExtensions.cs @@ -0,0 +1,70 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// MemoryReaderExtensions.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.IO; + +namespace Neo.Extensions +{ + /// + /// A helper class for serialization of NEO objects. + /// + public static class MemoryReaderExtensions + { + /// + /// Reads an array from a . + /// + /// The type of the array element. + /// The for reading data. + /// The maximum number of elements in the array. + /// The array read from the . + public static T[] ReadNullableArray(this ref MemoryReader reader, int max = 0x1000000) + where T : class, ISerializable, new() + { + var array = new T[reader.ReadVarInt((ulong)max)]; + for (var i = 0; i < array.Length; i++) + array[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + return array; + } + + /// + /// Reads an object from a . + /// + /// The type of the object. + /// The for reading data. + /// The object read from the . + public static T ReadSerializable(this ref MemoryReader reader) + where T : ISerializable, new() + { + T obj = new(); + obj.Deserialize(ref reader); + return obj; + } + + /// + /// Reads an array from a . + /// + /// The type of the array element. + /// The for reading data. + /// The maximum number of elements in the array. + /// The array read from the . + public static T[] ReadSerializableArray(this ref MemoryReader reader, int max = 0x1000000) + where T : ISerializable, new() + { + var array = new T[reader.ReadVarInt((ulong)max)]; + for (var i = 0; i < array.Length; i++) + { + array[i] = new T(); + array[i].Deserialize(ref reader); + } + return array; + } + } +} diff --git a/src/Neo/Extensions/MemoryExtensions.cs b/src/Neo/Extensions/MemoryExtensions.cs index f6576387de..c1331a5998 100644 --- a/src/Neo/Extensions/MemoryExtensions.cs +++ b/src/Neo/Extensions/MemoryExtensions.cs @@ -17,6 +17,20 @@ namespace Neo.Extensions { public static class MemoryExtensions { + /// + /// Converts a byte array to an array. + /// + /// The type of the array element. + /// The byte array to be converted. + /// The maximum number of elements contained in the converted array. + /// The converted array. + public static T[] AsSerializableArray(this ReadOnlyMemory value, int max = 0x1000000) where T : ISerializable, new() + { + if (value.IsEmpty) throw new FormatException(); + MemoryReader reader = new(value); + return reader.ReadSerializableArray(max); + } + /// /// Converts a byte array to an object. /// diff --git a/src/Neo/Extensions/SpanExtensions.cs b/src/Neo/Extensions/SpanExtensions.cs new file mode 100644 index 0000000000..3bc650cbb8 --- /dev/null +++ b/src/Neo/Extensions/SpanExtensions.cs @@ -0,0 +1,80 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SpanExtensions.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 K4os.Compression.LZ4; +using System; +using System.Buffers.Binary; + +namespace Neo.Extensions +{ + public static class SpanExtensions + { + /// + /// Compresses the specified data using the LZ4 algorithm. + /// + /// The data to be compressed. + /// The compressed data. + public static ReadOnlyMemory CompressLz4(this ReadOnlySpan data) + { + var maxLength = LZ4Codec.MaximumOutputSize(data.Length); + var buffer = new byte[sizeof(uint) + maxLength]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, data.Length); + var length = LZ4Codec.Encode(data, buffer.AsSpan(sizeof(uint))); + return buffer.AsMemory(0, sizeof(uint) + length); + } + + /// + /// Compresses the specified data using the LZ4 algorithm. + /// + /// The data to be compressed. + /// The compressed data. + public static ReadOnlyMemory CompressLz4(this Span data) + { + var maxLength = LZ4Codec.MaximumOutputSize(data.Length); + var buffer = new byte[sizeof(uint) + maxLength]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, data.Length); + var length = LZ4Codec.Encode(data, buffer.AsSpan(sizeof(uint))); + return buffer.AsMemory(0, sizeof(uint) + length); + } + + /// + /// Decompresses the specified data using the LZ4 algorithm. + /// + /// The compressed data. + /// The maximum data size after decompression. + /// The original data. + public static byte[] DecompressLz4(this ReadOnlySpan data, int maxOutput) + { + var length = BinaryPrimitives.ReadInt32LittleEndian(data); + if (length < 0 || length > maxOutput) throw new FormatException(); + var result = new byte[length]; + if (LZ4Codec.Decode(data[4..], result) != length) + throw new FormatException(); + return result; + } + + /// + /// Decompresses the specified data using the LZ4 algorithm. + /// + /// The compressed data. + /// The maximum data size after decompression. + /// The original data. + public static byte[] DecompressLz4(this Span data, int maxOutput) + { + var length = BinaryPrimitives.ReadInt32LittleEndian(data); + if (length < 0 || length > maxOutput) throw new FormatException(); + var result = new byte[length]; + if (LZ4Codec.Decode(data[4..], result) != length) + throw new FormatException(); + return result; + } + } +} diff --git a/src/Neo/Extensions/UInt160Extensions.cs b/src/Neo/Extensions/UInt160Extensions.cs new file mode 100644 index 0000000000..988d7e5d83 --- /dev/null +++ b/src/Neo/Extensions/UInt160Extensions.cs @@ -0,0 +1,32 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UInt160Extensions.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.VM; + +namespace Neo.Extensions +{ + public static class UInt160Extensions + { + /// + /// Generates the script for calling a contract dynamically. + /// + /// The hash of the contract to be called. + /// The method to be called in the contract. + /// The arguments for calling the contract. + /// The generated script. + public static byte[] MakeScript(this UInt160 scriptHash, string method, params object[] args) + { + using ScriptBuilder sb = new(); + sb.EmitDynamicCall(scriptHash, method, args); + return sb.ToArray(); + } + } +} diff --git a/src/Neo/Extensions/VM/EvaluationStackExtensions.cs b/src/Neo/Extensions/VM/EvaluationStackExtensions.cs new file mode 100644 index 0000000000..30684c01b2 --- /dev/null +++ b/src/Neo/Extensions/VM/EvaluationStackExtensions.cs @@ -0,0 +1,37 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// EvaluationStackExtensions.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.Json; +using Neo.VM; +using System; + +namespace Neo.Extensions +{ + public static class EvaluationStackExtensions + { + /// + /// Converts the to a JSON object. + /// + /// The to convert. + /// The maximum size in bytes of the result. + /// The represented by a JSON object. + public static JArray ToJson(this EvaluationStack stack, int maxSize = int.MaxValue) + { + if (maxSize <= 0) throw new ArgumentOutOfRangeException(nameof(maxSize)); + maxSize -= 2/*[]*/+ Math.Max(0, (stack.Count - 1))/*,*/; + JArray result = []; + foreach (var item in stack) + result.Add(item.ToJson(null, ref maxSize)); + if (maxSize < 0) throw new InvalidOperationException("Max size reached."); + return result; + } + } +} diff --git a/src/Neo/Extensions/VM/ScriptBuilderExtensions.cs b/src/Neo/Extensions/VM/ScriptBuilderExtensions.cs new file mode 100644 index 0000000000..ee64bb5736 --- /dev/null +++ b/src/Neo/Extensions/VM/ScriptBuilderExtensions.cs @@ -0,0 +1,306 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ScriptBuilderExtensions.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.ECC; +using Neo.IO; +using Neo.SmartContract; +using Neo.VM; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +namespace Neo.Extensions +{ + public static class ScriptBuilderExtensions + { + /// + /// Emits the opcodes for creating an array. + /// + /// The type of the elements of the array. + /// The to be used. + /// The elements of the array. + /// The same instance as . + public static ScriptBuilder CreateArray(this ScriptBuilder builder, IReadOnlyList list = null) + { + if (list is null || list.Count == 0) + return builder.Emit(OpCode.NEWARRAY0); + for (var i = list.Count - 1; i >= 0; i--) + builder.EmitPush(list[i]); + builder.EmitPush(list.Count); + return builder.Emit(OpCode.PACK); + } + + /// + /// Emits the opcodes for creating a map. + /// + /// The type of the key of the map. + /// The type of the value of the map. + /// The to be used. + /// The key/value pairs of the map. + /// The same instance as . + public static ScriptBuilder CreateMap(this ScriptBuilder builder, IEnumerable> map) + where TKey : notnull + where TValue : notnull + { + var count = map.Count(); + + if (count == 0) + return builder.Emit(OpCode.NEWMAP); + + foreach (var (key, value) in map.Reverse()) + { + builder.EmitPush(value); + builder.EmitPush(key); + } + builder.EmitPush(count); + return builder.Emit(OpCode.PACKMAP); + } + + /// + /// Emits the opcodes for creating a map. + /// + /// The type of the key of the map. + /// The type of the value of the map. + /// The to be used. + /// The key/value pairs of the map. + /// The same instance as . + public static ScriptBuilder CreateMap(this ScriptBuilder builder, IReadOnlyDictionary map) + where TKey : notnull + where TValue : notnull + { + if (map.Count == 0) + return builder.Emit(OpCode.NEWMAP); + + foreach (var (key, value) in map.Reverse()) + { + builder.EmitPush(value); + builder.EmitPush(key); + } + builder.EmitPush(map.Count); + return builder.Emit(OpCode.PACKMAP); + } + + /// + /// Emits the opcodes for creating a struct. + /// + /// The type of the property. + /// The to be used. + /// The list of properties. + /// The same instance as . + public static ScriptBuilder CreateStruct(this ScriptBuilder builder, IReadOnlyList array) + where T : notnull + { + if (array.Count == 0) + return builder.Emit(OpCode.NEWSTRUCT0); + for (var i = array.Count - 1; i >= 0; i--) + builder.EmitPush(array[i]); + builder.EmitPush(array.Count); + return builder.Emit(OpCode.PACKSTRUCT); + } + + /// + /// Emits the specified opcodes. + /// + /// The to be used. + /// The opcodes to emit. + /// The same instance as . + public static ScriptBuilder Emit(this ScriptBuilder builder, params OpCode[] ops) + { + foreach (var op in ops) + builder.Emit(op); + return builder; + } + + /// + /// Emits the opcodes for calling a contract dynamically. + /// + /// The to be used. + /// The hash of the contract to be called. + /// The method to be called in the contract. + /// The arguments for calling the contract. + /// The same instance as . + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder builder, UInt160 scriptHash, string method, params object[] args) + { + return EmitDynamicCall(builder, scriptHash, method, CallFlags.All, args); + } + + /// + /// Emits the opcodes for calling a contract dynamically. + /// + /// The to be used. + /// The hash of the contract to be called. + /// The method to be called in the contract. + /// The for calling the contract. + /// The arguments for calling the contract. + /// The same instance as . + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder builder, UInt160 scriptHash, string method, CallFlags flags, params object[] args) + { + builder.CreateArray(args); + builder.EmitPush(flags); + builder.EmitPush(method); + builder.EmitPush(scriptHash); + builder.EmitSysCall(ApplicationEngine.System_Contract_Call); + return builder; + } + + /// + /// Emits the opcodes for pushing the specified data onto the stack. + /// + /// The to be used. + /// The data to be pushed. + /// The same instance as . + public static ScriptBuilder EmitPush(this ScriptBuilder builder, ISerializable data) + { + return builder.EmitPush(data.ToArray()); + } + + /// + /// Emits the opcodes for pushing the specified data onto the stack. + /// + /// The to be used. + /// The data to be pushed. + /// The same instance as . + public static ScriptBuilder EmitPush(this ScriptBuilder builder, ContractParameter parameter) + { + if (parameter.Value is null) + builder.Emit(OpCode.PUSHNULL); + else + switch (parameter.Type) + { + case ContractParameterType.Signature: + case ContractParameterType.ByteArray: + builder.EmitPush((byte[])parameter.Value); + break; + case ContractParameterType.Boolean: + builder.EmitPush((bool)parameter.Value); + break; + case ContractParameterType.Integer: + if (parameter.Value is BigInteger bi) + builder.EmitPush(bi); + else + builder.EmitPush((BigInteger)typeof(BigInteger).GetConstructor([parameter.Value.GetType()]).Invoke([parameter.Value])); + break; + case ContractParameterType.Hash160: + builder.EmitPush((UInt160)parameter.Value); + break; + case ContractParameterType.Hash256: + builder.EmitPush((UInt256)parameter.Value); + break; + case ContractParameterType.PublicKey: + builder.EmitPush((ECPoint)parameter.Value); + break; + case ContractParameterType.String: + builder.EmitPush((string)parameter.Value); + break; + case ContractParameterType.Array: + { + var parameters = (IList)parameter.Value; + for (var i = parameters.Count - 1; i >= 0; i--) + builder.EmitPush(parameters[i]); + builder.EmitPush(parameters.Count); + builder.Emit(OpCode.PACK); + } + break; + case ContractParameterType.Map: + { + var pairs = (IList>)parameter.Value; + builder.CreateMap(pairs); + } + break; + default: + throw new ArgumentException(null, nameof(parameter)); + } + return builder; + } + + /// + /// Emits the opcodes for pushing the specified data onto the stack. + /// + /// The to be used. + /// The data to be pushed. + /// The same instance as . + public static ScriptBuilder EmitPush(this ScriptBuilder builder, object obj) + { + switch (obj) + { + case bool data: + builder.EmitPush(data); + break; + case byte[] data: + builder.EmitPush(data); + break; + case string data: + builder.EmitPush(data); + break; + case BigInteger data: + builder.EmitPush(data); + break; + case ISerializable data: + builder.EmitPush(data); + break; + case sbyte data: + builder.EmitPush(data); + break; + case byte data: + builder.EmitPush(data); + break; + case short data: + builder.EmitPush(data); + break; + case char data: + builder.EmitPush(data); + break; + case ushort data: + builder.EmitPush(data); + break; + case int data: + builder.EmitPush(data); + break; + case uint data: + builder.EmitPush(data); + break; + case long data: + builder.EmitPush(data); + break; + case ulong data: + builder.EmitPush(data); + break; + case Enum data: + builder.EmitPush(BigInteger.Parse(data.ToString("d"))); + break; + case ContractParameter data: + builder.EmitPush(data); + break; + case null: + builder.Emit(OpCode.PUSHNULL); + break; + default: + throw new ArgumentException(null, nameof(obj)); + } + return builder; + } + + /// + /// Emits the opcodes for invoking an interoperable service. + /// + /// The to be used. + /// The hash of the interoperable service. + /// The arguments for calling the interoperable service. + /// The same instance as . + public static ScriptBuilder EmitSysCall(this ScriptBuilder builder, uint method, params object[] args) + { + for (var i = args.Length - 1; i >= 0; i--) + EmitPush(builder, args[i]); + return builder.EmitSysCall(method); + } + } +} diff --git a/src/Neo/IEventHandlers/ICommittedHandler.cs b/src/Neo/IEventHandlers/ICommittedHandler.cs index 632e88916b..2818f3f4e6 100644 --- a/src/Neo/IEventHandlers/ICommittedHandler.cs +++ b/src/Neo/IEventHandlers/ICommittedHandler.cs @@ -12,15 +12,16 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; -namespace Neo.IEventHandlers; - -public interface ICommittedHandler +namespace Neo.IEventHandlers { - /// - /// This is the handler of Commited event from - /// Triggered after a new block is Commited, and state has being updated. - /// - /// The object. - /// The committed . - void Blockchain_Committed_Handler(NeoSystem system, Block block); + public interface ICommittedHandler + { + /// + /// This is the handler of Commited event from + /// Triggered after a new block is Commited, and state has being updated. + /// + /// The object. + /// The committed . + void Blockchain_Committed_Handler(NeoSystem system, Block block); + } } diff --git a/src/Neo/IEventHandlers/ICommittingHandler.cs b/src/Neo/IEventHandlers/ICommittingHandler.cs index 3fcccccda2..41038ccf73 100644 --- a/src/Neo/IEventHandlers/ICommittingHandler.cs +++ b/src/Neo/IEventHandlers/ICommittingHandler.cs @@ -14,17 +14,18 @@ using Neo.Persistence; using System.Collections.Generic; -namespace Neo.IEventHandlers; - -public interface ICommittingHandler +namespace Neo.IEventHandlers { - /// - /// This is the handler of Committing event from - /// Triggered when a new block is committing, and the state is still in the cache. - /// - /// The instance associated with the event. - /// The block that is being committed. - /// The current data snapshot. - /// A list of executed applications associated with the block. - void Blockchain_Committing_Handler(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList); + public interface ICommittingHandler + { + /// + /// This is the handler of Committing event from + /// Triggered when a new block is committing, and the state is still in the cache. + /// + /// The instance associated with the event. + /// The block that is being committed. + /// The current data snapshot. + /// A list of executed applications associated with the block. + void Blockchain_Committing_Handler(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList); + } } diff --git a/src/Neo/IEventHandlers/ILogHandler.cs b/src/Neo/IEventHandlers/ILogHandler.cs index ae0a1f9773..c09c1f539b 100644 --- a/src/Neo/IEventHandlers/ILogHandler.cs +++ b/src/Neo/IEventHandlers/ILogHandler.cs @@ -11,15 +11,16 @@ using Neo.SmartContract; -namespace Neo.IEventHandlers; - -public interface ILogHandler +namespace Neo.IEventHandlers { - /// - /// The handler of Log event from the . - /// Triggered when a contract calls System.Runtime.Log. - /// - /// The source of the event. - /// The arguments of the log. - void ApplicationEngine_Log_Handler(object sender, LogEventArgs logEventArgs); + public interface ILogHandler + { + /// + /// The handler of Log event from the . + /// Triggered when a contract calls System.Runtime.Log. + /// + /// The source of the event. + /// The arguments of the log. + void ApplicationEngine_Log_Handler(object sender, LogEventArgs logEventArgs); + } } diff --git a/src/Neo/IEventHandlers/ILoggingHandler.cs b/src/Neo/IEventHandlers/ILoggingHandler.cs index 7ec03d4751..de4391477a 100644 --- a/src/Neo/IEventHandlers/ILoggingHandler.cs +++ b/src/Neo/IEventHandlers/ILoggingHandler.cs @@ -9,16 +9,17 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.IEventHandlers; - -public interface ILoggingHandler +namespace Neo.IEventHandlers { - /// - /// The handler of Logging event from - /// Triggered when a new log is added by calling - /// - /// The source of the log. Used to identify the producer of the log. - /// The level of the log. - /// The message of the log. - void Utility_Logging_Handler(string source, LogLevel level, object message); + public interface ILoggingHandler + { + /// + /// The handler of Logging event from + /// Triggered when a new log is added by calling + /// + /// The source of the log. Used to identify the producer of the log. + /// The level of the log. + /// The message of the log. + void Utility_Logging_Handler(string source, LogLevel level, object message); + } } diff --git a/src/Neo/IEventHandlers/IMessageReceivedHandler.cs b/src/Neo/IEventHandlers/IMessageReceivedHandler.cs index e4e15a4f3f..3d7e429f96 100644 --- a/src/Neo/IEventHandlers/IMessageReceivedHandler.cs +++ b/src/Neo/IEventHandlers/IMessageReceivedHandler.cs @@ -11,15 +11,16 @@ using Neo.Network.P2P; -namespace Neo.IEventHandlers; - -public interface IMessageReceivedHandler +namespace Neo.IEventHandlers { - /// - /// The handler of MessageReceived event from - /// Triggered when a new message is received from a peer - /// - /// The object - /// The current node received from a peer - bool RemoteNode_MessageReceived_Handler(NeoSystem system, Message message); + public interface IMessageReceivedHandler + { + /// + /// The handler of MessageReceived event from + /// Triggered when a new message is received from a peer + /// + /// The object + /// The current node received from a peer + bool RemoteNode_MessageReceived_Handler(NeoSystem system, Message message); + } } diff --git a/src/Neo/IEventHandlers/INotifyHandler.cs b/src/Neo/IEventHandlers/INotifyHandler.cs index 36b9c4abe4..430373ba67 100644 --- a/src/Neo/IEventHandlers/INotifyHandler.cs +++ b/src/Neo/IEventHandlers/INotifyHandler.cs @@ -11,15 +11,16 @@ using Neo.SmartContract; -namespace Neo.IEventHandlers; - -public interface INotifyHandler +namespace Neo.IEventHandlers { - /// - /// The handler of Notify event from - /// Triggered when a contract calls System.Runtime.Notify. - /// - /// The source of the event. - /// The arguments of the notification. - void ApplicationEngine_Notify_Handler(object sender, NotifyEventArgs notifyEventArgs); + public interface INotifyHandler + { + /// + /// The handler of Notify event from + /// Triggered when a contract calls System.Runtime.Notify. + /// + /// The source of the event. + /// The arguments of the notification. + void ApplicationEngine_Notify_Handler(object sender, NotifyEventArgs notifyEventArgs); + } } diff --git a/src/Neo/IEventHandlers/IServiceAddedHandler.cs b/src/Neo/IEventHandlers/IServiceAddedHandler.cs index 7c3b66a471..e48b153b85 100644 --- a/src/Neo/IEventHandlers/IServiceAddedHandler.cs +++ b/src/Neo/IEventHandlers/IServiceAddedHandler.cs @@ -9,15 +9,16 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.IEventHandlers; - -public interface IServiceAddedHandler +namespace Neo.IEventHandlers { - /// - /// The handler of ServiceAdded event from the . - /// Triggered when a service is added to the . - /// - /// The source of the event. - /// The service added. - void NeoSystem_ServiceAdded_Handler(object sender, object service); + public interface IServiceAddedHandler + { + /// + /// The handler of ServiceAdded event from the . + /// Triggered when a service is added to the . + /// + /// The source of the event. + /// The service added. + void NeoSystem_ServiceAdded_Handler(object sender, object service); + } } diff --git a/src/Neo/IEventHandlers/ITransactionAddedHandler.cs b/src/Neo/IEventHandlers/ITransactionAddedHandler.cs index e120170c3d..a0a698c719 100644 --- a/src/Neo/IEventHandlers/ITransactionAddedHandler.cs +++ b/src/Neo/IEventHandlers/ITransactionAddedHandler.cs @@ -12,15 +12,16 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; -namespace Neo.IEventHandlers; - -public interface ITransactionAddedHandler +namespace Neo.IEventHandlers { - /// - /// The handler of TransactionAdded event from the . - /// Triggered when a transaction is added to the . - /// - /// The source of the event - /// The transaction added to the memory pool . - void MemoryPool_TransactionAdded_Handler(object sender, Transaction tx); + public interface ITransactionAddedHandler + { + /// + /// The handler of TransactionAdded event from the . + /// Triggered when a transaction is added to the . + /// + /// The source of the event + /// The transaction added to the memory pool . + void MemoryPool_TransactionAdded_Handler(object sender, Transaction tx); + } } diff --git a/src/Neo/IEventHandlers/ITransactionRemovedHandler.cs b/src/Neo/IEventHandlers/ITransactionRemovedHandler.cs index 077aa3eccb..ea18fc4bed 100644 --- a/src/Neo/IEventHandlers/ITransactionRemovedHandler.cs +++ b/src/Neo/IEventHandlers/ITransactionRemovedHandler.cs @@ -11,15 +11,16 @@ using Neo.Ledger; -namespace Neo.IEventHandlers; - -public interface ITransactionRemovedHandler +namespace Neo.IEventHandlers { - /// - /// Handler of TransactionRemoved event from - /// Triggered when a transaction is removed to the . - /// - /// The source of the event - /// The arguments of event that removes a transaction from the - void MemoryPool_TransactionRemoved_Handler(object sender, TransactionRemovedEventArgs tx); + public interface ITransactionRemovedHandler + { + /// + /// Handler of TransactionRemoved event from + /// Triggered when a transaction is removed to the . + /// + /// The source of the event + /// The arguments of event that removes a transaction from the + void MemoryPool_TransactionRemoved_Handler(object sender, TransactionRemovedEventArgs tx); + } } diff --git a/src/Neo/IEventHandlers/IWalletChangedHandler.cs b/src/Neo/IEventHandlers/IWalletChangedHandler.cs index 32986e6e15..979a7be227 100644 --- a/src/Neo/IEventHandlers/IWalletChangedHandler.cs +++ b/src/Neo/IEventHandlers/IWalletChangedHandler.cs @@ -11,15 +11,16 @@ using Neo.Wallets; -namespace Neo.IEventHandlers; - -public interface IWalletChangedHandler +namespace Neo.IEventHandlers { - /// - /// The handler of WalletChanged event from the . - /// Triggered when a new wallet is assigned to the node. - /// - /// The source of the event - /// The new wallet being assigned to the system. - void IWalletProvider_WalletChanged_Handler(object sender, Wallet wallet); + public interface IWalletChangedHandler + { + /// + /// The handler of WalletChanged event from the . + /// Triggered when a new wallet is assigned to the node. + /// + /// The source of the event + /// The new wallet being assigned to the system. + void IWalletProvider_WalletChanged_Handler(object sender, Wallet wallet); + } } diff --git a/src/Neo/IO/Caching/ECPointCache.cs b/src/Neo/IO/Caching/ECPointCache.cs index a32671d30f..e875b190af 100644 --- a/src/Neo/IO/Caching/ECPointCache.cs +++ b/src/Neo/IO/Caching/ECPointCache.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; namespace Neo.IO.Caching { diff --git a/src/Neo/IO/Helper.cs b/src/Neo/IO/Helper.cs deleted file mode 100644 index d0e5a00ec9..0000000000 --- a/src/Neo/IO/Helper.cs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// Helper.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 K4os.Compression.LZ4; -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.IO; - -namespace Neo.IO -{ - /// - /// A helper class for serialization of NEO objects. - /// - public static class Helper - { - /// - /// Converts a byte array to an object. - /// - /// The type to convert to. - /// The byte array to be converted. - /// The offset into the byte array from which to begin using data. - /// The converted object. - public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() - { - MemoryReader reader = new(value.AsMemory(start)); - return reader.ReadSerializable(); - } - - /// - /// Converts a byte array to an array. - /// - /// The type of the array element. - /// The byte array to be converted. - /// The maximum number of elements contained in the converted array. - /// The converted array. - public static T[] AsSerializableArray(this byte[] value, int max = 0x1000000) where T : ISerializable, new() - { - MemoryReader reader = new(value); - return reader.ReadSerializableArray(max); - } - - /// - /// Converts a byte array to an array. - /// - /// The type of the array element. - /// The byte array to be converted. - /// The maximum number of elements contained in the converted array. - /// The converted array. - public static T[] AsSerializableArray(this ReadOnlyMemory value, int max = 0x1000000) where T : ISerializable, new() - { - if (value.IsEmpty) throw new FormatException(); - MemoryReader reader = new(value); - return reader.ReadSerializableArray(max); - } - - /// - /// Compresses the specified data using the LZ4 algorithm. - /// - /// The data to be compressed. - /// The compressed data. - public static ReadOnlyMemory CompressLz4(this ReadOnlySpan data) - { - int maxLength = LZ4Codec.MaximumOutputSize(data.Length); - byte[] buffer = new byte[sizeof(uint) + maxLength]; - BinaryPrimitives.WriteInt32LittleEndian(buffer, data.Length); - int length = LZ4Codec.Encode(data, buffer.AsSpan(sizeof(uint))); - return buffer.AsMemory(0, sizeof(uint) + length); - } - - /// - /// Decompresses the specified data using the LZ4 algorithm. - /// - /// The compressed data. - /// The maximum data size after decompression. - /// The original data. - public static byte[] DecompressLz4(this ReadOnlySpan data, int maxOutput) - { - int length = BinaryPrimitives.ReadInt32LittleEndian(data); - if (length < 0 || length > maxOutput) throw new FormatException(); - byte[] result = new byte[length]; - if (LZ4Codec.Decode(data[4..], result) != length) - throw new FormatException(); - return result; - } - - /// - /// Reads a byte array of the specified size from a . - /// - /// The for reading data. - /// The size of the byte array. - /// The byte array read from the . - public static byte[] ReadFixedBytes(this BinaryReader reader, int size) - { - var index = 0; - var data = new byte[size]; - - while (size > 0) - { - var bytesRead = reader.Read(data, index, size); - - if (bytesRead <= 0) - { - throw new FormatException(); - } - - size -= bytesRead; - index += bytesRead; - } - - return data; - } - - /// - /// Reads an array from a . - /// - /// The type of the array element. - /// The for reading data. - /// The maximum number of elements in the array. - /// The array read from the . - public static T[] ReadNullableArray(this ref MemoryReader reader, int max = 0x1000000) where T : class, ISerializable, new() - { - T[] array = new T[reader.ReadVarInt((ulong)max)]; - for (int i = 0; i < array.Length; i++) - array[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - return array; - } - - /// - /// Reads an object from a . - /// - /// The type of the object. - /// The for reading data. - /// The object read from the . - public static T ReadSerializable(this ref MemoryReader reader) where T : ISerializable, new() - { - T obj = new(); - obj.Deserialize(ref reader); - return obj; - } - - /// - /// Reads an array from a . - /// - /// The type of the array element. - /// The for reading data. - /// The maximum number of elements in the array. - /// The array read from the . - public static T[] ReadSerializableArray(this ref MemoryReader reader, int max = 0x1000000) where T : ISerializable, new() - { - T[] array = new T[reader.ReadVarInt((ulong)max)]; - for (int i = 0; i < array.Length; i++) - { - array[i] = new T(); - array[i].Deserialize(ref reader); - } - return array; - } - - /// - /// Reads a byte array from a . - /// - /// The for reading data. - /// The maximum size of the byte array. - /// The byte array read from the . - public static byte[] ReadVarBytes(this BinaryReader reader, int max = 0x1000000) - { - return reader.ReadFixedBytes((int)reader.ReadVarInt((ulong)max)); - } - - /// - /// Reads an integer from a . - /// - /// The for reading data. - /// The maximum value of the integer. - /// The integer read from the . - public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxValue) - { - byte fb = reader.ReadByte(); - ulong value; - if (fb == 0xFD) - value = reader.ReadUInt16(); - else if (fb == 0xFE) - value = reader.ReadUInt32(); - else if (fb == 0xFF) - value = reader.ReadUInt64(); - else - value = fb; - if (value > max) throw new FormatException(); - return value; - } - - /// - /// Converts an object to a byte array. - /// - /// The object to be converted. - /// The converted byte array. - public static byte[] ToArray(this ISerializable value) - { - using MemoryStream ms = new(); - using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); - value.Serialize(writer); - writer.Flush(); - return ms.ToArray(); - } - - /// - /// Writes an object into a . - /// - /// The for writing data. - /// The object to be written. - public static void Write(this BinaryWriter writer, ISerializable value) - { - value.Serialize(writer); - } - - /// - /// Writes an array into a . - /// - /// The type of the array element. - /// The for writing data. - /// The array to be written. - public static void Write(this BinaryWriter writer, IReadOnlyCollection value) where T : ISerializable - { - writer.WriteVarInt(value.Count); - foreach (T item in value) - { - item.Serialize(writer); - } - } - - /// - /// Writes a into a . - /// - /// The for writing data. - /// The to be written. - /// The fixed size of the . - public static void WriteFixedString(this BinaryWriter writer, string value, int length) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - if (value.Length > length) - throw new ArgumentException(null, nameof(value)); - byte[] bytes = Utility.StrictUTF8.GetBytes(value); - if (bytes.Length > length) - throw new ArgumentException(null, nameof(value)); - writer.Write(bytes); - if (bytes.Length < length) - writer.Write(stackalloc byte[length - bytes.Length]); - } - - /// - /// Writes an array into a . - /// - /// The type of the array element. - /// The for writing data. - /// The array to be written. - public static void WriteNullableArray(this BinaryWriter writer, T[] value) where T : class, ISerializable - { - writer.WriteVarInt(value.Length); - foreach (var item in value) - { - bool isNull = item is null; - writer.Write(!isNull); - if (isNull) continue; - item.Serialize(writer); - } - } - - /// - /// Writes a byte array into a . - /// - /// The for writing data. - /// The byte array to be written. - public static void WriteVarBytes(this BinaryWriter writer, ReadOnlySpan value) - { - writer.WriteVarInt(value.Length); - writer.Write(value); - } - - /// - /// Writes an integer into a . - /// - /// The for writing data. - /// The integer to be written. - public static void WriteVarInt(this BinaryWriter writer, long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value)); - if (value < 0xFD) - { - writer.Write((byte)value); - } - else if (value <= 0xFFFF) - { - writer.Write((byte)0xFD); - writer.Write((ushort)value); - } - else if (value <= 0xFFFFFFFF) - { - writer.Write((byte)0xFE); - writer.Write((uint)value); - } - else - { - writer.Write((byte)0xFF); - writer.Write(value); - } - } - - /// - /// Writes a into a . - /// - /// The for writing data. - /// The to be written. - public static void WriteVarString(this BinaryWriter writer, string value) - { - writer.WriteVarBytes(Utility.StrictUTF8.GetBytes(value)); - } - } -} diff --git a/src/Neo/Ledger/TransactionRemovedEventArgs.cs b/src/Neo/Ledger/TransactionRemovedEventArgs.cs index 403b24a195..b3265244ed 100644 --- a/src/Neo/Ledger/TransactionRemovedEventArgs.cs +++ b/src/Neo/Ledger/TransactionRemovedEventArgs.cs @@ -12,20 +12,21 @@ using Neo.Network.P2P.Payloads; using System.Collections.Generic; -namespace Neo.Ledger; - -/// -/// Represents the event data of . -/// -public sealed class TransactionRemovedEventArgs +namespace Neo.Ledger { /// - /// The s that is being removed. + /// Represents the event data of . /// - public IReadOnlyCollection Transactions { get; init; } + public sealed class TransactionRemovedEventArgs + { + /// + /// The s that is being removed. + /// + public IReadOnlyCollection Transactions { get; init; } - /// - /// The reason a transaction was removed. - /// - public TransactionRemovalReason Reason { get; init; } + /// + /// The reason a transaction was removed. + /// + public TransactionRemovalReason Reason { get; init; } + } } diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj index 89c200e31b..5092f442fc 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0 + netstandard2.1;net9.0 true Neo NEO;AntShares;Blockchain;Smart Contract @@ -9,14 +9,13 @@ - + - - - - - + + + + diff --git a/src/Neo/NeoSystem.cs b/src/Neo/NeoSystem.cs index 99be387712..acc2047f57 100644 --- a/src/Neo/NeoSystem.cs +++ b/src/Neo/NeoSystem.cs @@ -221,7 +221,7 @@ public void EnsureStopped(IActorRef actor) using Inbox inbox = Inbox.Create(ActorSystem); inbox.Watch(actor); ActorSystem.Stop(actor); - inbox.Receive(TimeSpan.FromMinutes(5)); + inbox.Receive(TimeSpan.FromSeconds(30)); } /// diff --git a/src/Neo/Network/P2P/Helper.cs b/src/Neo/Network/P2P/Helper.cs index f171a7b0a3..ffbbc6a702 100644 --- a/src/Neo/Network/P2P/Helper.cs +++ b/src/Neo/Network/P2P/Helper.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography; -using Neo.IO; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using System.IO; diff --git a/src/Neo/Network/P2P/Message.cs b/src/Neo/Network/P2P/Message.cs index 96c79f2b10..23caaf967a 100644 --- a/src/Neo/Network/P2P/Message.cs +++ b/src/Neo/Network/P2P/Message.cs @@ -144,7 +144,7 @@ internal static int TryDeserialize(ByteString data, out Message msg) payloadIndex += 8; } - if (length > PayloadMaxSize) throw new FormatException(); + if (length > PayloadMaxSize) throw new FormatException($"Invalid payload length: {length}."); if (data.Count < (int)length + payloadIndex) return 0; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs index 5fc1ba4381..0c8df2ddb7 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs @@ -18,13 +18,14 @@ using System; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { /// /// Represents the condition that all conditions must be met. /// - public class AndCondition : WitnessCondition + public class AndCondition : WitnessCondition, IEquatable { /// /// The expressions of the condition. @@ -34,6 +35,29 @@ public class AndCondition : WitnessCondition public override int Size => base.Size + Expressions.GetVarSize(); public override WitnessConditionType Type => WitnessConditionType.And; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(AndCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Expressions.SequenceEqual(other.Expressions); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is AndCondition ac && Equals(ac); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Expressions); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { if (maxNestDepth <= 0) throw new FormatException(); @@ -73,5 +97,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(new VM.Types.Array(referenceCounter, Expressions.Select(p => p.ToStackItem(referenceCounter)))); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(AndCondition left, AndCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(AndCondition left, AndCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs index 011a7466fe..4299bae04b 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs @@ -14,11 +14,13 @@ using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; +using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { - public class BooleanCondition : WitnessCondition + public class BooleanCondition : WitnessCondition, IEquatable { /// /// The expression of the . @@ -28,6 +30,29 @@ public class BooleanCondition : WitnessCondition public override int Size => base.Size + sizeof(bool); public override WitnessConditionType Type => WitnessConditionType.Boolean; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(BooleanCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Expression == other.Expression; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is BooleanCondition bc && Equals(bc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Expression); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Expression = reader.ReadBoolean(); @@ -61,5 +86,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(Expression); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(BooleanCondition left, BooleanCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(BooleanCondition left, BooleanCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs index b27c66931d..9cccec95cc 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs @@ -9,16 +9,19 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; +using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { - public class CalledByContractCondition : WitnessCondition + public class CalledByContractCondition : WitnessCondition, IEquatable { /// /// The script hash to be checked. @@ -28,6 +31,29 @@ public class CalledByContractCondition : WitnessCondition public override int Size => base.Size + UInt160.Length; public override WitnessConditionType Type => WitnessConditionType.CalledByContract; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CalledByContractCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Hash == other.Hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is CalledByContractCondition cc && Equals(cc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Hash.GetHashCode()); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Hash = reader.ReadSerializable(); @@ -61,5 +87,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(Hash.ToArray()); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CalledByContractCondition left, CalledByContractCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CalledByContractCondition left, CalledByContractCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs index 8f8e21f80a..75124ab7cf 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByEntryCondition.cs @@ -12,14 +12,37 @@ using Neo.IO; using Neo.Json; using Neo.SmartContract; +using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { - public class CalledByEntryCondition : WitnessCondition + public class CalledByEntryCondition : WitnessCondition, IEquatable { public override WitnessConditionType Type => WitnessConditionType.CalledByEntry; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CalledByEntryCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return Type == other.Type; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is CalledByEntryCondition cc && Equals(cc); + } + + public override int GetHashCode() + { + return (byte)Type; + } + public override bool Match(ApplicationEngine engine) { var state = engine.CurrentContext.GetState(); @@ -33,5 +56,23 @@ protected override void DeserializeWithoutType(ref MemoryReader reader, int maxN protected override void SerializeWithoutType(BinaryWriter writer) { } private protected override void ParseJson(JObject json, int maxNestDepth) { } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CalledByEntryCondition left, CalledByEntryCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CalledByEntryCondition left, CalledByEntryCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs index 652eabd105..d4a3b74800 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs @@ -10,18 +10,21 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; using Neo.VM.Types; +using System; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { - public class CalledByGroupCondition : WitnessCondition + public class CalledByGroupCondition : WitnessCondition, IEquatable { /// /// The group to be checked. @@ -31,6 +34,29 @@ public class CalledByGroupCondition : WitnessCondition public override int Size => base.Size + Group.Size; public override WitnessConditionType Type => WitnessConditionType.CalledByGroup; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CalledByGroupCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Group == other.Group; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is CalledByGroupCondition cc && Equals(cc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Group); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Group = reader.ReadSerializable(); @@ -66,5 +92,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(Group.ToArray()); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CalledByGroupCondition left, CalledByGroupCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CalledByGroupCondition left, CalledByGroupCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs index be26fc0863..6e76309432 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs @@ -10,18 +10,21 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; using Neo.VM.Types; +using System; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { - public class GroupCondition : WitnessCondition + public class GroupCondition : WitnessCondition, IEquatable { /// /// The group to be checked. @@ -31,6 +34,29 @@ public class GroupCondition : WitnessCondition public override int Size => base.Size + Group.Size; public override WitnessConditionType Type => WitnessConditionType.Group; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(GroupCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Group == other.Group; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is GroupCondition gc && Equals(gc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Group); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Group = reader.ReadSerializable(); @@ -66,5 +92,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(Group.ToArray()); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(GroupCondition left, GroupCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(GroupCondition left, GroupCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs index 74ab01d21b..c8fd2baf69 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; @@ -16,13 +17,14 @@ using Neo.VM.Types; using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { /// /// Reverse another condition. /// - public class NotCondition : WitnessCondition + public class NotCondition : WitnessCondition, IEquatable { /// /// The expression of the condition to be reversed. @@ -32,6 +34,29 @@ public class NotCondition : WitnessCondition public override int Size => base.Size + Expression.Size; public override WitnessConditionType Type => WitnessConditionType.Not; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(NotCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Expression.Equals(other.Expression); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is NotCondition nc && Equals(nc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Expression.GetHashCode()); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { if (maxNestDepth <= 0) throw new FormatException(); @@ -67,5 +92,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(Expression.ToStackItem(referenceCounter)); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(NotCondition left, NotCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(NotCondition left, NotCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs index c28d3dbb94..177fb949fe 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs @@ -18,13 +18,14 @@ using System; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { /// /// Represents the condition that any of the conditions meets. /// - public class OrCondition : WitnessCondition + public class OrCondition : WitnessCondition, IEquatable { /// /// The expressions of the condition. @@ -34,6 +35,29 @@ public class OrCondition : WitnessCondition public override int Size => base.Size + Expressions.GetVarSize(); public override WitnessConditionType Type => WitnessConditionType.Or; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(OrCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Expressions.SequenceEqual(other.Expressions); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is OrCondition oc && Equals(oc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Expressions); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { if (maxNestDepth <= 0) throw new FormatException(); @@ -73,5 +97,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(new VM.Types.Array(referenceCounter, Expressions.Select(p => p.ToStackItem(referenceCounter)))); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(OrCondition left, OrCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(OrCondition left, OrCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs index 5d648efbc0..cde011c116 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs @@ -9,16 +9,19 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; using Neo.VM; using Neo.VM.Types; +using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { - public class ScriptHashCondition : WitnessCondition + public class ScriptHashCondition : WitnessCondition, IEquatable { /// /// The script hash to be checked. @@ -28,6 +31,29 @@ public class ScriptHashCondition : WitnessCondition public override int Size => base.Size + UInt160.Length; public override WitnessConditionType Type => WitnessConditionType.ScriptHash; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(ScriptHashCondition other) + { + if (ReferenceEquals(this, other)) + return true; + if (other is null) return false; + return + Type == other.Type && + Hash == other.Hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is ScriptHashCondition sc && Equals(sc); + } + + public override int GetHashCode() + { + return HashCode.Combine(Type, Hash); + } + protected override void DeserializeWithoutType(ref MemoryReader reader, int maxNestDepth) { Hash = reader.ReadSerializable(); @@ -61,5 +87,23 @@ public override StackItem ToStackItem(IReferenceCounter referenceCounter) result.Add(Hash.ToArray()); return result; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ScriptHashCondition left, ScriptHashCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(ScriptHashCondition left, ScriptHashCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs index dd955fce42..5fb5808714 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs @@ -17,6 +17,7 @@ using Neo.VM.Types; using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads.Conditions { @@ -32,6 +33,10 @@ public abstract class WitnessCondition : IInteroperable, ISerializable public virtual int Size => sizeof(WitnessConditionType); + public abstract override bool Equals(object obj); + + public abstract override int GetHashCode(); + void ISerializable.Deserialize(ref MemoryReader reader) { if (reader.ReadByte() != (byte)Type) throw new FormatException(); @@ -131,5 +136,23 @@ public virtual StackItem ToStackItem(IReferenceCounter referenceCounter) { return new VM.Types.Array(referenceCounter, new StackItem[] { (byte)Type }); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(WitnessCondition left, WitnessCondition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(WitnessCondition left, WitnessCondition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Conflicts.cs b/src/Neo/Network/P2P/Payloads/Conflicts.cs index 082de2014d..a876468828 100644 --- a/src/Neo/Network/P2P/Payloads/Conflicts.cs +++ b/src/Neo/Network/P2P/Payloads/Conflicts.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Persistence; diff --git a/src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs index 6e2b94a17d..080f5f55ab 100644 --- a/src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -86,7 +86,7 @@ Witness[] IVerifiable.Witnesses } set { - if (value.Length != 1) throw new ArgumentException(); + if (value.Length != 1) throw new ArgumentException($"Expected 1 witness, got {value.Length}."); Witness = value[0]; } } @@ -94,7 +94,9 @@ Witness[] IVerifiable.Witnesses void ISerializable.Deserialize(ref MemoryReader reader) { ((IVerifiable)this).DeserializeUnsigned(ref reader); - if (reader.ReadByte() != 1) throw new FormatException(); + var count = reader.ReadByte(); + if (count != 1) + throw new FormatException($"Expected 1 witness, got {count}."); Witness = reader.ReadSerializable(); } @@ -103,7 +105,8 @@ void IVerifiable.DeserializeUnsigned(ref MemoryReader reader) Category = reader.ReadVarString(32); ValidBlockStart = reader.ReadUInt32(); ValidBlockEnd = reader.ReadUInt32(); - if (ValidBlockStart >= ValidBlockEnd) throw new FormatException(); + if (ValidBlockStart >= ValidBlockEnd) + throw new FormatException($"Invalid valid block range: {ValidBlockStart} >= {ValidBlockEnd}."); Sender = reader.ReadSerializable(); Data = reader.ReadVarMemory(); } diff --git a/src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs b/src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs index 9d8d2b6b6c..5bb7e107e4 100644 --- a/src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs +++ b/src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs @@ -52,7 +52,7 @@ void ISerializable.Deserialize(ref MemoryReader reader) IndexStart = reader.ReadUInt32(); Count = reader.ReadInt16(); if (Count < -1 || Count == 0 || Count > HeadersPayload.MaxHeadersCount) - throw new FormatException(); + throw new FormatException($"Invalid count: {Count}/{HeadersPayload.MaxHeadersCount}."); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs b/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs index d12c8ae2cd..aeb6212f23 100644 --- a/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs +++ b/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System; using System.IO; @@ -51,7 +52,8 @@ void ISerializable.Deserialize(ref MemoryReader reader) { HashStart = reader.ReadSerializable(); Count = reader.ReadInt16(); - if (Count < -1 || Count == 0) throw new FormatException(); + if (Count < -1 || Count == 0) + throw new FormatException($"Invalid count: {Count}."); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/Neo/Network/P2P/Payloads/Header.cs b/src/Neo/Network/P2P/Payloads/Header.cs index 9e1d77fb8a..8b3eda0e28 100644 --- a/src/Neo/Network/P2P/Payloads/Header.cs +++ b/src/Neo/Network/P2P/Payloads/Header.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Ledger; diff --git a/src/Neo/Network/P2P/Payloads/InvPayload.cs b/src/Neo/Network/P2P/Payloads/InvPayload.cs index 17625f6dc4..891c4a15e6 100644 --- a/src/Neo/Network/P2P/Payloads/InvPayload.cs +++ b/src/Neo/Network/P2P/Payloads/InvPayload.cs @@ -60,16 +60,14 @@ public static InvPayload Create(InventoryType type, params UInt256[] hashes) /// The type of the inventories. /// The hashes of the inventories. /// The created payloads. - public static IEnumerable CreateGroup(InventoryType type, UInt256[] hashes) + public static IEnumerable CreateGroup(InventoryType type, IReadOnlyCollection hashes) { - for (int i = 0; i < hashes.Length; i += MaxHashesCount) + foreach (var chunk in hashes.Chunk(MaxHashesCount)) { - int endIndex = i + MaxHashesCount; - if (endIndex > hashes.Length) endIndex = hashes.Length; yield return new InvPayload { Type = type, - Hashes = hashes[i..endIndex] + Hashes = chunk, }; } } diff --git a/src/Neo/Network/P2P/Payloads/OracleResponse.cs b/src/Neo/Network/P2P/Payloads/OracleResponse.cs index bf3aa70cf4..9a938b52de 100644 --- a/src/Neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/Neo/Network/P2P/Payloads/OracleResponse.cs @@ -72,7 +72,8 @@ protected override void DeserializeWithoutType(ref MemoryReader reader) Id = reader.ReadUInt64(); Code = (OracleResponseCode)reader.ReadByte(); if (!Enum.IsDefined(typeof(OracleResponseCode), Code)) - throw new FormatException(); + throw new FormatException($"Invalid response code: {Code}."); + Result = reader.ReadVarMemory(MaxResultSize); if (Code != OracleResponseCode.Success && Result.Length > 0) throw new FormatException(); diff --git a/src/Neo/Network/P2P/Payloads/Signer.cs b/src/Neo/Network/P2P/Payloads/Signer.cs index 40496dfe74..eadc623c0a 100644 --- a/src/Neo/Network/P2P/Payloads/Signer.cs +++ b/src/Neo/Network/P2P/Payloads/Signer.cs @@ -20,13 +20,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads { /// /// Represents a signer of a . /// - public class Signer : IInteroperable, ISerializable + public class Signer : IInteroperable, ISerializable, IEquatable { // This limits maximum number of AllowedContracts or AllowedGroups here private const int MaxSubitems = 16; @@ -66,6 +67,39 @@ public class Signer : IInteroperable, ISerializable /*AllowedGroups*/ (Scopes.HasFlag(WitnessScope.CustomGroups) ? AllowedGroups.GetVarSize() : 0) + /*Rules*/ (Scopes.HasFlag(WitnessScope.WitnessRules) ? Rules.GetVarSize() : 0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Signer other) + { + if (ReferenceEquals(this, other)) + return true; + + if (other is null) return false; + if (Account != other.Account || Scopes != other.Scopes) + return false; + + if (Scopes.HasFlag(WitnessScope.CustomContracts) && !AllowedContracts.SequenceEqual(other.AllowedContracts)) + return false; + + if (Scopes.HasFlag(WitnessScope.CustomGroups) && !AllowedGroups.SequenceEqual(other.AllowedGroups)) + return false; + + if (Scopes.HasFlag(WitnessScope.WitnessRules) && !Rules.SequenceEqual(other.Rules)) + return false; + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + return obj is Signer signerObj && Equals(signerObj); + } + + public override int GetHashCode() + { + return HashCode.Combine(Account.GetHashCode(), Scopes); + } + public void Deserialize(ref MemoryReader reader) { Account = reader.ReadSerializable(); @@ -111,7 +145,7 @@ public IEnumerable GetAllRules() } if (Scopes.HasFlag(WitnessScope.CustomContracts)) { - foreach (UInt160 hash in AllowedContracts) + foreach (var hash in AllowedContracts) yield return new WitnessRule { Action = WitnessRuleAction.Allow, @@ -120,7 +154,7 @@ public IEnumerable GetAllRules() } if (Scopes.HasFlag(WitnessScope.CustomGroups)) { - foreach (ECPoint group in AllowedGroups) + foreach (var group in AllowedGroups) yield return new WitnessRule { Action = WitnessRuleAction.Allow, @@ -129,7 +163,7 @@ public IEnumerable GetAllRules() } if (Scopes.HasFlag(WitnessScope.WitnessRules)) { - foreach (WitnessRule rule in Rules) + foreach (var rule in Rules) yield return rule; } } @@ -202,5 +236,23 @@ VM.Types.StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter Scopes.HasFlag(WitnessScope.WitnessRules) ? new VM.Types.Array(referenceCounter, Rules.Select(u => u.ToStackItem(referenceCounter))) : new VM.Types.Array(referenceCounter) ]); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Signer left, Signer right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Signer left, Signer right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/Payloads/Transaction.cs b/src/Neo/Network/P2P/Payloads/Transaction.cs index b5139f1c7c..d1984d8387 100644 --- a/src/Neo/Network/P2P/Payloads/Transaction.cs +++ b/src/Neo/Network/P2P/Payloads/Transaction.cs @@ -231,13 +231,18 @@ private static Signer[] DeserializeSigners(ref MemoryReader reader, int maxCount public void DeserializeUnsigned(ref MemoryReader reader) { Version = reader.ReadByte(); - if (Version > 0) throw new FormatException(); + if (Version > 0) throw new FormatException($"Invalid version: {Version}."); + Nonce = reader.ReadUInt32(); SystemFee = reader.ReadInt64(); - if (SystemFee < 0) throw new FormatException(); + if (SystemFee < 0) throw new FormatException($"Invalid system fee: {SystemFee}."); + NetworkFee = reader.ReadInt64(); - if (NetworkFee < 0) throw new FormatException(); - if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); + if (NetworkFee < 0) throw new FormatException($"Invalid network fee: {NetworkFee}."); + + if (SystemFee + NetworkFee < SystemFee) + throw new FormatException($"Invalid fee: {SystemFee} + {NetworkFee} < {SystemFee}."); + ValidUntilBlock = reader.ReadUInt32(); Signers = DeserializeSigners(ref reader, MaxTransactionAttributes); Attributes = DeserializeAttributes(ref reader, MaxTransactionAttributes - Signers.Length); diff --git a/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs index 34af3453fe..6c9a94312f 100644 --- a/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/Neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -38,8 +38,9 @@ public abstract class TransactionAttribute : ISerializable public void Deserialize(ref MemoryReader reader) { - if (reader.ReadByte() != (byte)Type) - throw new FormatException(); + var type = reader.ReadByte(); + if (type != (byte)Type) + throw new FormatException($"Expected {Type}, got {type}."); DeserializeWithoutType(ref reader); } @@ -52,7 +53,7 @@ public static TransactionAttribute DeserializeFrom(ref MemoryReader reader) { TransactionAttributeType type = (TransactionAttributeType)reader.ReadByte(); if (ReflectionCache.CreateInstance(type) is not TransactionAttribute attribute) - throw new FormatException(); + throw new FormatException($"Invalid attribute type: {type}."); attribute.DeserializeWithoutType(ref reader); return attribute; } diff --git a/src/Neo/Network/P2P/Payloads/WitnessRule.cs b/src/Neo/Network/P2P/Payloads/WitnessRule.cs index 8552f0798e..ddc1d108cf 100644 --- a/src/Neo/Network/P2P/Payloads/WitnessRule.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRule.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; @@ -17,13 +18,14 @@ using Neo.VM.Types; using System; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.Network.P2P.Payloads { /// /// The rule used to describe the scope of the witness. /// - public class WitnessRule : IInteroperable, ISerializable + public class WitnessRule : IInteroperable, ISerializable, IEquatable { /// /// Indicates the action to be taken if the current context meets with the rule. @@ -37,11 +39,32 @@ public class WitnessRule : IInteroperable, ISerializable int ISerializable.Size => sizeof(WitnessRuleAction) + Condition.Size; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(WitnessRule other) + { + if (ReferenceEquals(this, other)) return true; + if (other is null) return false; + return Action == other.Action && + Condition == other.Condition; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + return obj is WitnessRule wr && Equals(wr); + } + + public override int GetHashCode() + { + return HashCode.Combine(Action, Condition.GetHashCode()); + } + void ISerializable.Deserialize(ref MemoryReader reader) { Action = (WitnessRuleAction)reader.ReadByte(); if (Action != WitnessRuleAction.Allow && Action != WitnessRuleAction.Deny) - throw new FormatException(); + throw new FormatException($"Invalid action: {Action}."); Condition = WitnessCondition.DeserializeFrom(ref reader, WitnessCondition.MaxNestingDepth); } @@ -59,9 +82,8 @@ void ISerializable.Serialize(BinaryWriter writer) public static WitnessRule FromJson(JObject json) { WitnessRuleAction action = Enum.Parse(json["action"].GetString()); - if (action != WitnessRuleAction.Allow && action != WitnessRuleAction.Deny) - throw new FormatException(); + throw new FormatException($"Invalid action: {action}."); return new() { @@ -96,5 +118,23 @@ public StackItem ToStackItem(IReferenceCounter referenceCounter) Condition.ToStackItem(referenceCounter) }); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(WitnessRule left, WitnessRule right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(WitnessRule left, WitnessRule right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 4e7861a562..0aa42a5280 100644 --- a/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -278,7 +278,7 @@ private void OnGetDataMessageReceived(InvPayload payload) if (notFound.Count > 0) { - foreach (InvPayload entry in InvPayload.CreateGroup(payload.Type, notFound.ToArray())) + foreach (InvPayload entry in InvPayload.CreateGroup(payload.Type, notFound)) EnqueueMessage(Message.Create(MessageCommand.NotFound, entry)); } } diff --git a/src/Neo/Network/P2P/RemoteNode.cs b/src/Neo/Network/P2P/RemoteNode.cs index a4bf0cc089..e570afeb13 100644 --- a/src/Neo/Network/P2P/RemoteNode.cs +++ b/src/Neo/Network/P2P/RemoteNode.cs @@ -13,6 +13,7 @@ using Akka.Configuration; using Akka.IO; using Neo.Cryptography; +using Neo.Extensions; using Neo.IO; using Neo.IO.Actors; using Neo.IO.Caching; diff --git a/src/Neo/Network/P2P/TaskManager.cs b/src/Neo/Network/P2P/TaskManager.cs index c5708df923..fec92a93ec 100644 --- a/src/Neo/Network/P2P/TaskManager.cs +++ b/src/Neo/Network/P2P/TaskManager.cs @@ -133,7 +133,7 @@ private void OnNewTasks(InvPayload payload) session.InvTasks[hash] = TimeProvider.Current.UtcNow; } - foreach (InvPayload group in InvPayload.CreateGroup(payload.Type, hashes.ToArray())) + foreach (InvPayload group in InvPayload.CreateGroup(payload.Type, hashes)) Sender.Tell(Message.Create(MessageCommand.GetData, group)); } @@ -317,18 +317,9 @@ private void OnTimer() { foreach (TaskSession session in sessions.Values) { - foreach (var (hash, time) in session.InvTasks.ToArray()) - if (TimeProvider.Current.UtcNow - time > TaskTimeout) - { - if (session.InvTasks.Remove(hash)) - DecrementGlobalTask(hash); - } - foreach (var (index, time) in session.IndexTasks.ToArray()) - if (TimeProvider.Current.UtcNow - time > TaskTimeout) - { - if (session.IndexTasks.Remove(index)) - DecrementGlobalTask(index); - } + var now = TimeProvider.Current.UtcNow; + session.InvTasks.RemoveWhere(p => now - p.Value > TaskTimeout, p => DecrementGlobalTask(p.Key)); + session.IndexTasks.RemoveWhere(p => now - p.Value > TaskTimeout, p => DecrementGlobalTask(p.Key)); } foreach (var (actor, session) in sessions) RequestTasks(actor, session); @@ -365,15 +356,13 @@ private void RequestTasks(IActorRef remoteNode, TaskSession session) HashSet hashes = new(session.AvailableTasks); if (hashes.Count > 0) { - foreach (UInt256 hash in hashes.ToArray()) - { - if (!IncrementGlobalTask(hash)) - hashes.Remove(hash); - } + hashes.RemoveWhere(p => !IncrementGlobalTask(p)); session.AvailableTasks.Remove(hashes); + foreach (UInt256 hash in hashes) session.InvTasks[hash] = DateTime.UtcNow; - foreach (InvPayload group in InvPayload.CreateGroup(InventoryType.Block, hashes.ToArray())) + + foreach (InvPayload group in InvPayload.CreateGroup(InventoryType.Block, hashes)) remoteNode.Tell(Message.Create(MessageCommand.GetData, group)); return; } diff --git a/src/Neo/Persistence/SnapshotCache.cs b/src/Neo/Persistence/SnapshotCache.cs index 6731e5342f..07afdccfab 100644 --- a/src/Neo/Persistence/SnapshotCache.cs +++ b/src/Neo/Persistence/SnapshotCache.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.SmartContract; using System; using System.Collections.Generic; diff --git a/src/Neo/Persistence/StoreFactory.cs b/src/Neo/Persistence/StoreFactory.cs index 6fee81e9f3..e892129894 100644 --- a/src/Neo/Persistence/StoreFactory.cs +++ b/src/Neo/Persistence/StoreFactory.cs @@ -11,49 +11,50 @@ using System.Collections.Generic; -namespace Neo.Persistence; - -public static class StoreFactory +namespace Neo.Persistence { - private static readonly Dictionary providers = new(); - - static StoreFactory() + public static class StoreFactory { - var memProvider = new MemoryStoreProvider(); - RegisterProvider(memProvider); + private static readonly Dictionary providers = new(); - // Default cases - providers.Add("", memProvider); - } + static StoreFactory() + { + var memProvider = new MemoryStoreProvider(); + RegisterProvider(memProvider); - public static void RegisterProvider(IStoreProvider provider) - { - providers.Add(provider.Name, provider); - } + // Default cases + providers.Add("", memProvider); + } - /// - /// Get store provider by name - /// - /// Name - /// Store provider - public static IStoreProvider GetStoreProvider(string name) - { - if (providers.TryGetValue(name, out var provider)) + public static void RegisterProvider(IStoreProvider provider) { - return provider; + providers.Add(provider.Name, provider); } - return null; - } + /// + /// Get store provider by name + /// + /// Name + /// Store provider + public static IStoreProvider GetStoreProvider(string name) + { + if (providers.TryGetValue(name, out var provider)) + { + return provider; + } - /// - /// Get store from name - /// - /// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used. - /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. - /// The storage engine. - public static IStore GetStore(string storageProvider, string path) - { - return providers[storageProvider].GetStore(path); + return null; + } + + /// + /// Get store from name + /// + /// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used. + /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. + /// The storage engine. + public static IStore GetStore(string storageProvider, string path) + { + return providers[storageProvider].GetStore(path); + } } } diff --git a/src/Neo/Plugins/PluginSettings.cs b/src/Neo/Plugins/PluginSettings.cs index 66549a390e..64efbfc8f7 100644 --- a/src/Neo/Plugins/PluginSettings.cs +++ b/src/Neo/Plugins/PluginSettings.cs @@ -13,21 +13,22 @@ using Org.BouncyCastle.Security; using System; -namespace Neo.Plugins; - -public abstract class PluginSettings(IConfigurationSection section) +namespace Neo.Plugins { - public UnhandledExceptionPolicy ExceptionPolicy + public abstract class PluginSettings(IConfigurationSection section) { - get + public UnhandledExceptionPolicy ExceptionPolicy { - var policyString = section.GetValue(nameof(UnhandledExceptionPolicy), nameof(UnhandledExceptionPolicy.StopNode)); - if (Enum.TryParse(policyString, true, out UnhandledExceptionPolicy policy)) + get { - return policy; - } + var policyString = section.GetValue(nameof(UnhandledExceptionPolicy), nameof(UnhandledExceptionPolicy.StopNode)); + if (Enum.TryParse(policyString, true, out UnhandledExceptionPolicy policy)) + { + return policy; + } - throw new InvalidParameterException($"{policyString} is not a valid UnhandledExceptionPolicy"); + throw new InvalidParameterException($"{policyString} is not a valid UnhandledExceptionPolicy"); + } } } } diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs index 7808baae5e..f8f2332b4e 100644 --- a/src/Neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; diff --git a/src/Neo/SmartContract/BinarySerializer.cs b/src/Neo/SmartContract/BinarySerializer.cs index b77a998d6e..46b2a644a1 100644 --- a/src/Neo/SmartContract/BinarySerializer.cs +++ b/src/Neo/SmartContract/BinarySerializer.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/ContractParametersContext.cs b/src/Neo/SmartContract/ContractParametersContext.cs index c4129f0ad1..ea5ea35ab8 100644 --- a/src/Neo/SmartContract/ContractParametersContext.cs +++ b/src/Neo/SmartContract/ContractParametersContext.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; diff --git a/src/Neo/SmartContract/ContractState.cs b/src/Neo/SmartContract/ContractState.cs index cd065cb8c3..8acb6504a8 100644 --- a/src/Neo/SmartContract/ContractState.cs +++ b/src/Neo/SmartContract/ContractState.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.SmartContract.Manifest; using Neo.VM; diff --git a/src/Neo/SmartContract/Helper.cs b/src/Neo/SmartContract/Helper.cs index d0bc3c939b..96ec2af03c 100644 --- a/src/Neo/SmartContract/Helper.cs +++ b/src/Neo/SmartContract/Helper.cs @@ -11,6 +11,7 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; diff --git a/src/Neo/SmartContract/KeyBuilder.cs b/src/Neo/SmartContract/KeyBuilder.cs index cfd27cc6f8..9e6371ff31 100644 --- a/src/Neo/SmartContract/KeyBuilder.cs +++ b/src/Neo/SmartContract/KeyBuilder.cs @@ -13,6 +13,7 @@ using System; using System.Buffers.Binary; using System.IO; +using System.Runtime.CompilerServices; namespace Neo.SmartContract { @@ -21,18 +22,20 @@ namespace Neo.SmartContract /// public class KeyBuilder { - private readonly MemoryStream stream = new(); + private readonly MemoryStream stream; /// /// Initializes a new instance of the class. /// /// The id of the contract. /// The prefix of the key. - public KeyBuilder(int id, byte prefix) + /// The hint of the storage key size(including the id and prefix). + public KeyBuilder(int id, byte prefix, int keySizeHint = ApplicationEngine.MaxStorageKeySize) { - var data = new byte[sizeof(int)]; + Span data = stackalloc byte[sizeof(int)]; BinaryPrimitives.WriteInt32LittleEndian(data, id); + stream = new(keySizeHint); stream.Write(data); stream.WriteByte(prefix); } @@ -42,6 +45,7 @@ public KeyBuilder(int id, byte prefix) /// /// Part of the key. /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyBuilder Add(byte key) { stream.WriteByte(key); @@ -53,12 +57,37 @@ public KeyBuilder Add(byte key) /// /// Part of the key. /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyBuilder Add(ReadOnlySpan key) { stream.Write(key); return this; } + /// + /// Adds part of the key to the builder. + /// + /// Part of the key represented by a byte array. + /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyBuilder Add(byte[] key) => Add(key.AsSpan()); + + /// + /// Adds part of the key to the builder. + /// + /// Part of the key represented by a . + /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyBuilder Add(UInt160 key) => Add(key.GetSpan()); + + /// + /// Adds part of the key to the builder. + /// + /// Part of the key represented by a . + /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyBuilder Add(UInt256 key) => Add(key.GetSpan()); + /// /// Adds part of the key to the builder. /// @@ -79,9 +108,10 @@ public KeyBuilder Add(ISerializable key) /// /// Part of the key. /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyBuilder AddBigEndian(int key) { - var data = new byte[sizeof(int)]; + Span data = stackalloc byte[sizeof(int)]; BinaryPrimitives.WriteInt32BigEndian(data, key); return Add(data); @@ -92,9 +122,10 @@ public KeyBuilder AddBigEndian(int key) /// /// Part of the key. /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyBuilder AddBigEndian(uint key) { - var data = new byte[sizeof(uint)]; + Span data = stackalloc byte[sizeof(uint)]; BinaryPrimitives.WriteUInt32BigEndian(data, key); return Add(data); @@ -105,9 +136,10 @@ public KeyBuilder AddBigEndian(uint key) /// /// Part of the key. /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyBuilder AddBigEndian(long key) { - var data = new byte[sizeof(long)]; + Span data = stackalloc byte[sizeof(long)]; BinaryPrimitives.WriteInt64BigEndian(data, key); return Add(data); @@ -118,9 +150,10 @@ public KeyBuilder AddBigEndian(long key) /// /// Part of the key. /// A reference to this instance after the add operation has completed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyBuilder AddBigEndian(ulong key) { - var data = new byte[sizeof(ulong)]; + Span data = stackalloc byte[sizeof(ulong)]; BinaryPrimitives.WriteUInt64BigEndian(data, key); return Add(data); diff --git a/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs index de77597655..9a3eabab06 100644 --- a/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -14,6 +14,7 @@ using Neo.VM.Types; using System; using System.Linq; +using System.Runtime.CompilerServices; using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Manifest @@ -21,7 +22,7 @@ namespace Neo.SmartContract.Manifest /// /// Represents an event in a smart contract ABI. /// - public class ContractEventDescriptor : IInteroperable + public class ContractEventDescriptor : IInteroperable, IEquatable { /// /// The name of the event or method. @@ -77,5 +78,44 @@ public virtual JObject ToJson() json["parameters"] = new JArray(Parameters.Select(u => u.ToJson()).ToArray()); return json; } + + public bool Equals(ContractEventDescriptor other) + { + if (other == null) return false; + if (ReferenceEquals(this, other)) return true; + + return Name == other.Name && Parameters.SequenceEqual(other.Parameters); + } + + public override bool Equals(object other) + { + if (other is not ContractEventDescriptor ev) + return false; + + return Equals(ev); + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Parameters); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ContractEventDescriptor left, ContractEventDescriptor right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(ContractEventDescriptor left, ContractEventDescriptor right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/SmartContract/Manifest/ContractGroup.cs b/src/Neo/SmartContract/Manifest/ContractGroup.cs index 8796c0f07a..3b8dc8ef20 100644 --- a/src/Neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/Neo/SmartContract/Manifest/ContractGroup.cs @@ -11,7 +11,7 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs index 56b36163de..6c024dc179 100644 --- a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -14,13 +14,14 @@ using Neo.VM.Types; using System; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.SmartContract.Manifest { /// /// Represents a method in a smart contract ABI. /// - public class ContractMethodDescriptor : ContractEventDescriptor + public class ContractMethodDescriptor : ContractEventDescriptor, IEquatable { /// /// Indicates the return type of the method. It can be any value of . @@ -90,5 +91,47 @@ public override JObject ToJson() json["safe"] = Safe; return json; } + + public bool Equals(ContractMethodDescriptor other) + { + if (ReferenceEquals(this, other)) return true; + + return + base.Equals(other) && // Already check null + ReturnType == other.ReturnType + && Offset == other.Offset + && Safe == other.Safe; + } + + public override bool Equals(object other) + { + if (other is not ContractMethodDescriptor ev) + return false; + + return Equals(ev); + } + + public override int GetHashCode() + { + return HashCode.Combine(ReturnType, Offset, Safe, base.GetHashCode()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ContractMethodDescriptor left, ContractMethodDescriptor right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(ContractMethodDescriptor left, ContractMethodDescriptor right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs index b36fa353fe..7579dd54e6 100644 --- a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -13,13 +13,14 @@ using Neo.VM; using Neo.VM.Types; using System; +using System.Runtime.CompilerServices; namespace Neo.SmartContract.Manifest { /// /// Represents a parameter of an event or method in ABI. /// - public class ContractParameterDefinition : IInteroperable + public class ContractParameterDefinition : IInteroperable, IEquatable { /// /// The name of the parameter. @@ -73,5 +74,44 @@ public JObject ToJson() json["type"] = Type.ToString(); return json; } + + public bool Equals(ContractParameterDefinition other) + { + if (other == null) return false; + if (ReferenceEquals(this, other)) return true; + + return Name == other.Name && Type == other.Type; + } + + public override bool Equals(object other) + { + if (other is not ContractParameterDefinition parm) + return false; + + return Equals(parm); + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Type); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ContractParameterDefinition left, ContractParameterDefinition right) + { + if (left is null || right is null) + return Equals(left, right); + + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(ContractParameterDefinition left, ContractParameterDefinition right) + { + if (left is null || right is null) + return !Equals(left, right); + + return !left.Equals(right); + } } } diff --git a/src/Neo/SmartContract/Manifest/ContractPermission.cs b/src/Neo/SmartContract/Manifest/ContractPermission.cs index cf4d5078c5..1888963672 100644 --- a/src/Neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermission.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index d950d25c62..6cebaa99ee 100644 --- a/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.VM.Types; using System; diff --git a/src/Neo/SmartContract/Native/ContractManagement.cs b/src/Neo/SmartContract/Native/ContractManagement.cs index 6fefd2d05f..9fb66a063c 100644 --- a/src/Neo/SmartContract/Native/ContractManagement.cs +++ b/src/Neo/SmartContract/Native/ContractManagement.cs @@ -11,7 +11,7 @@ #pragma warning disable IDE0051 -using Neo.IO; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Iterators; diff --git a/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs b/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs index f966a73fd1..9741aacaf8 100644 --- a/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs +++ b/src/Neo/SmartContract/Native/CryptoLib.BLS12_381.cs @@ -13,133 +13,134 @@ using Neo.VM.Types; using System; -namespace Neo.SmartContract.Native; - -partial class CryptoLib +namespace Neo.SmartContract.Native { - /// - /// Serialize a bls12381 point. - /// - /// The point to be serialized. - /// - [ContractMethod(CpuFee = 1 << 19)] - public static byte[] Bls12381Serialize(InteropInterface g) + partial class CryptoLib { - return g.GetInterface() switch + /// + /// Serialize a bls12381 point. + /// + /// The point to be serialized. + /// + [ContractMethod(CpuFee = 1 << 19)] + public static byte[] Bls12381Serialize(InteropInterface g) { - G1Affine p => p.ToCompressed(), - G1Projective p => new G1Affine(p).ToCompressed(), - G2Affine p => p.ToCompressed(), - G2Projective p => new G2Affine(p).ToCompressed(), - Gt p => p.ToArray(), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") - }; - } + return g.GetInterface() switch + { + G1Affine p => p.ToCompressed(), + G1Projective p => new G1Affine(p).ToCompressed(), + G2Affine p => p.ToCompressed(), + G2Projective p => new G2Affine(p).ToCompressed(), + Gt p => p.ToArray(), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } - /// - /// Deserialize a bls12381 point. - /// - /// The point as byte array. - /// - [ContractMethod(CpuFee = 1 << 19)] - public static InteropInterface Bls12381Deserialize(byte[] data) - { - return data.Length switch + /// + /// Deserialize a bls12381 point. + /// + /// The point as byte array. + /// + [ContractMethod(CpuFee = 1 << 19)] + public static InteropInterface Bls12381Deserialize(byte[] data) { - 48 => new InteropInterface(G1Affine.FromCompressed(data)), - 96 => new InteropInterface(G2Affine.FromCompressed(data)), - 576 => new InteropInterface(Gt.FromBytes(data)), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:valid point length"), - }; - } + return data.Length switch + { + 48 => new InteropInterface(G1Affine.FromCompressed(data)), + 96 => new InteropInterface(G2Affine.FromCompressed(data)), + 576 => new InteropInterface(Gt.FromBytes(data)), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:valid point length"), + }; + } - /// - /// Determines whether the specified points are equal. - /// - /// The first point. - /// Teh second point. - /// true if the specified points are equal; otherwise, false. - [ContractMethod(CpuFee = 1 << 5)] - public static bool Bls12381Equal(InteropInterface x, InteropInterface y) - { - return (x.GetInterface(), y.GetInterface()) switch + /// + /// Determines whether the specified points are equal. + /// + /// The first point. + /// Teh second point. + /// true if the specified points are equal; otherwise, false. + [ContractMethod(CpuFee = 1 << 5)] + public static bool Bls12381Equal(InteropInterface x, InteropInterface y) { - (G1Affine p1, G1Affine p2) => p1.Equals(p2), - (G1Projective p1, G1Projective p2) => p1.Equals(p2), - (G2Affine p1, G2Affine p2) => p1.Equals(p2), - (G2Projective p1, G2Projective p2) => p1.Equals(p2), - (Gt p1, Gt p2) => p1.Equals(p2), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") - }; - } + return (x.GetInterface(), y.GetInterface()) switch + { + (G1Affine p1, G1Affine p2) => p1.Equals(p2), + (G1Projective p1, G1Projective p2) => p1.Equals(p2), + (G2Affine p1, G2Affine p2) => p1.Equals(p2), + (G2Projective p1, G2Projective p2) => p1.Equals(p2), + (Gt p1, Gt p2) => p1.Equals(p2), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } - /// - /// Add operation of two points. - /// - /// The first point. - /// The second point. - /// - [ContractMethod(CpuFee = 1 << 19)] - public static InteropInterface Bls12381Add(InteropInterface x, InteropInterface y) - { - return (x.GetInterface(), y.GetInterface()) switch + /// + /// Add operation of two points. + /// + /// The first point. + /// The second point. + /// + [ContractMethod(CpuFee = 1 << 19)] + public static InteropInterface Bls12381Add(InteropInterface x, InteropInterface y) { - (G1Affine p1, G1Affine p2) => new(new G1Projective(p1) + p2), - (G1Affine p1, G1Projective p2) => new(p1 + p2), - (G1Projective p1, G1Affine p2) => new(p1 + p2), - (G1Projective p1, G1Projective p2) => new(p1 + p2), - (G2Affine p1, G2Affine p2) => new(new G2Projective(p1) + p2), - (G2Affine p1, G2Projective p2) => new(p1 + p2), - (G2Projective p1, G2Affine p2) => new(p1 + p2), - (G2Projective p1, G2Projective p2) => new(p1 + p2), - (Gt p1, Gt p2) => new(p1 + p2), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") - }; - } + return (x.GetInterface(), y.GetInterface()) switch + { + (G1Affine p1, G1Affine p2) => new(new G1Projective(p1) + p2), + (G1Affine p1, G1Projective p2) => new(p1 + p2), + (G1Projective p1, G1Affine p2) => new(p1 + p2), + (G1Projective p1, G1Projective p2) => new(p1 + p2), + (G2Affine p1, G2Affine p2) => new(new G2Projective(p1) + p2), + (G2Affine p1, G2Projective p2) => new(p1 + p2), + (G2Projective p1, G2Affine p2) => new(p1 + p2), + (G2Projective p1, G2Projective p2) => new(p1 + p2), + (Gt p1, Gt p2) => new(p1 + p2), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } - /// - /// Mul operation of gt point and multiplier - /// - /// The point - /// Multiplier,32 bytes,little-endian - /// negative number - /// - [ContractMethod(CpuFee = 1 << 21)] - public static InteropInterface Bls12381Mul(InteropInterface x, byte[] mul, bool neg) - { - Scalar X = neg ? -Scalar.FromBytes(mul) : Scalar.FromBytes(mul); - return x.GetInterface() switch + /// + /// Mul operation of gt point and multiplier + /// + /// The point + /// Multiplier,32 bytes,little-endian + /// negative number + /// + [ContractMethod(CpuFee = 1 << 21)] + public static InteropInterface Bls12381Mul(InteropInterface x, byte[] mul, bool neg) { - G1Affine p => new(p * X), - G1Projective p => new(p * X), - G2Affine p => new(p * X), - G2Projective p => new(p * X), - Gt p => new(p * X), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") - }; - } + Scalar X = neg ? -Scalar.FromBytes(mul) : Scalar.FromBytes(mul); + return x.GetInterface() switch + { + G1Affine p => new(p * X), + G1Projective p => new(p * X), + G2Affine p => new(p * X), + G2Projective p => new(p * X), + Gt p => new(p * X), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + } - /// - /// Pairing operation of g1 and g2 - /// - /// The g1 point. - /// The g2 point. - /// - [ContractMethod(CpuFee = 1 << 23)] - public static InteropInterface Bls12381Pairing(InteropInterface g1, InteropInterface g2) - { - G1Affine g1a = g1.GetInterface() switch - { - G1Affine g => g, - G1Projective g => new(g), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") - }; - G2Affine g2a = g2.GetInterface() switch + /// + /// Pairing operation of g1 and g2 + /// + /// The g1 point. + /// The g2 point. + /// + [ContractMethod(CpuFee = 1 << 23)] + public static InteropInterface Bls12381Pairing(InteropInterface g1, InteropInterface g2) { - G2Affine g => g, - G2Projective g => new(g), - _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") - }; - return new(Bls12.Pairing(in g1a, in g2a)); + G1Affine g1a = g1.GetInterface() switch + { + G1Affine g => g, + G1Projective g => new(g), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + G2Affine g2a = g2.GetInterface() switch + { + G2Affine g => g, + G2Projective g => new(g), + _ => throw new ArgumentException($"Bls12381 operation fault, type:format, error:type mismatch") + }; + return new(Bls12.Pairing(in g1a, in g2a)); + } } } diff --git a/src/Neo/SmartContract/Native/FungibleToken.cs b/src/Neo/SmartContract/Native/FungibleToken.cs index 21de0a5644..ab6ed8f9c0 100644 --- a/src/Neo/SmartContract/Native/FungibleToken.cs +++ b/src/Neo/SmartContract/Native/FungibleToken.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/Native/HashIndexState.cs b/src/Neo/SmartContract/Native/HashIndexState.cs index 4ab7a991cf..16c051e4aa 100644 --- a/src/Neo/SmartContract/Native/HashIndexState.cs +++ b/src/Neo/SmartContract/Native/HashIndexState.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/Native/LedgerContract.cs b/src/Neo/SmartContract/Native/LedgerContract.cs index 3d2d16e023..b10b7b7f0d 100644 --- a/src/Neo/SmartContract/Native/LedgerContract.cs +++ b/src/Neo/SmartContract/Native/LedgerContract.cs @@ -76,6 +76,9 @@ internal override ContractTask PostPersistAsync(ApplicationEngine engine) internal bool Initialized(DataCache snapshot) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + return snapshot.Find(CreateStorageKey(Prefix_Block).ToArray()).Any(); } @@ -94,6 +97,9 @@ private bool IsTraceableBlock(DataCache snapshot, uint index, uint maxTraceableB /// The hash of the block. public UInt256 GetBlockHash(DataCache snapshot, uint index) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_BlockHash).AddBigEndian(index)); if (item is null) return null; return new UInt256(item.Value.Span); @@ -107,6 +113,9 @@ public UInt256 GetBlockHash(DataCache snapshot, uint index) [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public UInt256 CurrentHash(DataCache snapshot) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Hash; } @@ -118,6 +127,9 @@ public UInt256 CurrentHash(DataCache snapshot) [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint CurrentIndex(DataCache snapshot) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Index; } @@ -129,6 +141,9 @@ public uint CurrentIndex(DataCache snapshot) /// if the blockchain contains the block; otherwise, . public bool ContainsBlock(DataCache snapshot, UInt256 hash) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + return snapshot.Contains(CreateStorageKey(Prefix_Block).Add(hash)); } @@ -155,6 +170,12 @@ public bool ContainsTransaction(DataCache snapshot, UInt256 hash) /// if the blockchain contains the hash of the conflicting transaction; otherwise, . public bool ContainsConflictHash(DataCache snapshot, UInt256 hash, IEnumerable signers, uint maxTraceableBlocks) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + + if (signers is null) + throw new ArgumentNullException(nameof(signers)); + // Check the dummy stub firstly to define whether there's exist at least one conflict record. var stub = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); if (stub is null || stub.Transaction is not null || !IsTraceableBlock(snapshot, stub.BlockIndex, maxTraceableBlocks)) @@ -179,6 +200,9 @@ public bool ContainsConflictHash(DataCache snapshot, UInt256 hash, IEnumerableThe trimmed block. public TrimmedBlock GetTrimmedBlock(DataCache snapshot, UInt256 hash) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_Block).Add(hash)); if (item is null) return null; return item.Value.AsSerializable(); @@ -262,6 +286,9 @@ public Header GetHeader(DataCache snapshot, uint index) /// The with the specified hash. public TransactionState GetTransactionState(DataCache snapshot, UInt256 hash) { + if (snapshot is null) + throw new ArgumentNullException(nameof(snapshot)); + var state = snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); if (state?.Transaction is null) return null; return state; diff --git a/src/Neo/SmartContract/Native/OracleContract.cs b/src/Neo/SmartContract/Native/OracleContract.cs index 54156a0029..c142b98ce1 100644 --- a/src/Neo/SmartContract/Native/OracleContract.cs +++ b/src/Neo/SmartContract/Native/OracleContract.cs @@ -12,7 +12,7 @@ #pragma warning disable IDE0051 using Neo.Cryptography; -using Neo.IO; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; diff --git a/src/Neo/SmartContract/Native/OracleRequest.cs b/src/Neo/SmartContract/Native/OracleRequest.cs index cd4962b1cb..746fada4e5 100644 --- a/src/Neo/SmartContract/Native/OracleRequest.cs +++ b/src/Neo/SmartContract/Native/OracleRequest.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.VM; using Neo.VM.Types; using Array = Neo.VM.Types.Array; diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs index e57f3f3e83..b11eeaf0dd 100644 --- a/src/Neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.IO; +using Neo.Extensions; using Neo.Persistence; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/NotifyEventArgs.cs b/src/Neo/SmartContract/NotifyEventArgs.cs index 8d8542bca9..2fe37affb3 100644 --- a/src/Neo/SmartContract/NotifyEventArgs.cs +++ b/src/Neo/SmartContract/NotifyEventArgs.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.VM; using Neo.VM.Types; diff --git a/src/Neo/SmartContract/StorageKey.cs b/src/Neo/SmartContract/StorageKey.cs index 7a643a2737..23ad2e1d17 100644 --- a/src/Neo/SmartContract/StorageKey.cs +++ b/src/Neo/SmartContract/StorageKey.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Cryptography; +using Neo.Extensions; using System; using System.Buffers.Binary; using System.Runtime.CompilerServices; diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index da4c920cbe..6aa4a9eaa9 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -101,6 +101,18 @@ public override int GetHashCode() return HashCode.Combine(_value1, _value2, _value3); } + /// + /// Gets a ReadOnlySpan that represents the current value in little-endian. + /// + /// A ReadOnlySpan that represents the current value in little-endian. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetSpan() + { + if (BitConverter.IsLittleEndian) + return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref _value1), Length); + return this.ToArray().AsSpan(); // Keep the same output as Serialize when BigEndian + } + /// /// Parses an from the specified . /// diff --git a/src/Neo/UInt256.cs b/src/Neo/UInt256.cs index 95324ef6ac..74f72d5cd3 100644 --- a/src/Neo/UInt256.cs +++ b/src/Neo/UInt256.cs @@ -14,6 +14,7 @@ using System; using System.Globalization; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Neo @@ -101,6 +102,18 @@ public override int GetHashCode() return (int)value1; } + /// + /// Gets a ReadOnlySpan that represents the current value in little-endian. + /// + /// A ReadOnlySpan that represents the current value in little-endian. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetSpan() + { + if (BitConverter.IsLittleEndian) + return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref value1), Length); + return this.ToArray().AsSpan(); // Keep the same output as Serialize when BigEndian + } + /// /// Parses an from the specified . /// diff --git a/src/Neo/VM/Helper.cs b/src/Neo/VM/Helper.cs index fd95467da1..2321d014b2 100644 --- a/src/Neo/VM/Helper.cs +++ b/src/Neo/VM/Helper.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.SmartContract; using Neo.VM.Types; @@ -29,301 +29,6 @@ namespace Neo.VM /// public static class Helper { - /// - /// Emits the opcodes for creating an array. - /// - /// The type of the elements of the array. - /// The to be used. - /// The elements of the array. - /// The same instance as . - public static ScriptBuilder CreateArray(this ScriptBuilder builder, IReadOnlyList list = null) - { - if (list is null || list.Count == 0) - return builder.Emit(OpCode.NEWARRAY0); - for (int i = list.Count - 1; i >= 0; i--) - builder.EmitPush(list[i]); - builder.EmitPush(list.Count); - return builder.Emit(OpCode.PACK); - } - - /// - /// Emits the opcodes for creating a map. - /// - /// The type of the key of the map. - /// The type of the value of the map. - /// The to be used. - /// The key/value pairs of the map. - /// The same instance as . - public static ScriptBuilder CreateMap(this ScriptBuilder builder, IEnumerable> map) - where TKey : notnull - where TValue : notnull - { - var count = map.Count(); - - if (count == 0) - return builder.Emit(OpCode.NEWMAP); - - foreach (var (key, value) in map.Reverse()) - { - builder.EmitPush(value); - builder.EmitPush(key); - } - builder.EmitPush(count); - return builder.Emit(OpCode.PACKMAP); - } - - /// - /// Emits the opcodes for creating a map. - /// - /// The type of the key of the map. - /// The type of the value of the map. - /// The to be used. - /// The key/value pairs of the map. - /// The same instance as . - public static ScriptBuilder CreateMap(this ScriptBuilder builder, IReadOnlyDictionary map) - where TKey : notnull - where TValue : notnull - { - if (map.Count == 0) - return builder.Emit(OpCode.NEWMAP); - - foreach (var (key, value) in map.Reverse()) - { - builder.EmitPush(value); - builder.EmitPush(key); - } - builder.EmitPush(map.Count); - return builder.Emit(OpCode.PACKMAP); - } - - /// - /// Emits the opcodes for creating a struct. - /// - /// The type of the property. - /// The to be used. - /// The list of properties. - /// The same instance as . - public static ScriptBuilder CreateStruct(this ScriptBuilder builder, IReadOnlyList array) - where T : notnull - { - if (array.Count == 0) - return builder.Emit(OpCode.NEWSTRUCT0); - for (var i = array.Count - 1; i >= 0; i--) - builder.EmitPush(array[i]); - builder.EmitPush(array.Count); - return builder.Emit(OpCode.PACKSTRUCT); - } - - /// - /// Emits the specified opcodes. - /// - /// The to be used. - /// The opcodes to emit. - /// The same instance as . - public static ScriptBuilder Emit(this ScriptBuilder builder, params OpCode[] ops) - { - foreach (OpCode op in ops) - builder.Emit(op); - return builder; - } - - /// - /// Emits the opcodes for calling a contract dynamically. - /// - /// The to be used. - /// The hash of the contract to be called. - /// The method to be called in the contract. - /// The arguments for calling the contract. - /// The same instance as . - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder builder, UInt160 scriptHash, string method, params object[] args) - { - return EmitDynamicCall(builder, scriptHash, method, CallFlags.All, args); - } - - /// - /// Emits the opcodes for calling a contract dynamically. - /// - /// The to be used. - /// The hash of the contract to be called. - /// The method to be called in the contract. - /// The for calling the contract. - /// The arguments for calling the contract. - /// The same instance as . - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder builder, UInt160 scriptHash, string method, CallFlags flags, params object[] args) - { - builder.CreateArray(args); - builder.EmitPush(flags); - builder.EmitPush(method); - builder.EmitPush(scriptHash); - builder.EmitSysCall(ApplicationEngine.System_Contract_Call); - return builder; - } - - /// - /// Emits the opcodes for pushing the specified data onto the stack. - /// - /// The to be used. - /// The data to be pushed. - /// The same instance as . - public static ScriptBuilder EmitPush(this ScriptBuilder builder, ISerializable data) - { - return builder.EmitPush(data.ToArray()); - } - - /// - /// Emits the opcodes for pushing the specified data onto the stack. - /// - /// The to be used. - /// The data to be pushed. - /// The same instance as . - public static ScriptBuilder EmitPush(this ScriptBuilder builder, ContractParameter parameter) - { - if (parameter.Value is null) - builder.Emit(OpCode.PUSHNULL); - else - switch (parameter.Type) - { - case ContractParameterType.Signature: - case ContractParameterType.ByteArray: - builder.EmitPush((byte[])parameter.Value); - break; - case ContractParameterType.Boolean: - builder.EmitPush((bool)parameter.Value); - break; - case ContractParameterType.Integer: - if (parameter.Value is BigInteger bi) - builder.EmitPush(bi); - else - builder.EmitPush((BigInteger)typeof(BigInteger).GetConstructor(new[] { parameter.Value.GetType() }).Invoke(new[] { parameter.Value })); - break; - case ContractParameterType.Hash160: - builder.EmitPush((UInt160)parameter.Value); - break; - case ContractParameterType.Hash256: - builder.EmitPush((UInt256)parameter.Value); - break; - case ContractParameterType.PublicKey: - builder.EmitPush((ECPoint)parameter.Value); - break; - case ContractParameterType.String: - builder.EmitPush((string)parameter.Value); - break; - case ContractParameterType.Array: - { - IList parameters = (IList)parameter.Value; - for (int i = parameters.Count - 1; i >= 0; i--) - builder.EmitPush(parameters[i]); - builder.EmitPush(parameters.Count); - builder.Emit(OpCode.PACK); - } - break; - case ContractParameterType.Map: - { - var pairs = (IList>)parameter.Value; - builder.CreateMap(pairs); - } - break; - default: - throw new ArgumentException(null, nameof(parameter)); - } - return builder; - } - - /// - /// Emits the opcodes for pushing the specified data onto the stack. - /// - /// The to be used. - /// The data to be pushed. - /// The same instance as . - public static ScriptBuilder EmitPush(this ScriptBuilder builder, object obj) - { - switch (obj) - { - case bool data: - builder.EmitPush(data); - break; - case byte[] data: - builder.EmitPush(data); - break; - case string data: - builder.EmitPush(data); - break; - case BigInteger data: - builder.EmitPush(data); - break; - case ISerializable data: - builder.EmitPush(data); - break; - case sbyte data: - builder.EmitPush(data); - break; - case byte data: - builder.EmitPush(data); - break; - case short data: - builder.EmitPush(data); - break; - case char data: - builder.EmitPush(data); - break; - case ushort data: - builder.EmitPush(data); - break; - case int data: - builder.EmitPush(data); - break; - case uint data: - builder.EmitPush(data); - break; - case long data: - builder.EmitPush(data); - break; - case ulong data: - builder.EmitPush(data); - break; - case Enum data: - builder.EmitPush(BigInteger.Parse(data.ToString("d"))); - break; - case ContractParameter data: - builder.EmitPush(data); - break; - case null: - builder.Emit(OpCode.PUSHNULL); - break; - default: - throw new ArgumentException(null, nameof(obj)); - } - return builder; - } - - /// - /// Emits the opcodes for invoking an interoperable service. - /// - /// The to be used. - /// The hash of the interoperable service. - /// The arguments for calling the interoperable service. - /// The same instance as . - public static ScriptBuilder EmitSysCall(this ScriptBuilder builder, uint method, params object[] args) - { - for (int i = args.Length - 1; i >= 0; i--) - EmitPush(builder, args[i]); - return builder.EmitSysCall(method); - } - - /// - /// Generates the script for calling a contract dynamically. - /// - /// The hash of the contract to be called. - /// The method to be called in the contract. - /// The arguments for calling the contract. - /// The generated script. - public static byte[] MakeScript(this UInt160 scriptHash, string method, params object[] args) - { - using ScriptBuilder sb = new(); - sb.EmitDynamicCall(scriptHash, method, args); - return sb.ToArray(); - } - /// /// Converts the to a JSON object. /// @@ -335,24 +40,7 @@ public static JObject ToJson(this StackItem item, int maxSize = int.MaxValue) return ToJson(item, null, ref maxSize); } - /// - /// Converts the to a JSON object. - /// - /// The to convert. - /// The maximum size in bytes of the result. - /// The represented by a JSON object. - public static JArray ToJson(this EvaluationStack stack, int maxSize = int.MaxValue) - { - if (maxSize <= 0) throw new ArgumentOutOfRangeException(nameof(maxSize)); - maxSize -= 2/*[]*/+ Math.Max(0, (stack.Count - 1))/*,*/; - JArray result = new(); - foreach (var item in stack) - result.Add(ToJson(item, null, ref maxSize)); - if (maxSize < 0) throw new InvalidOperationException("Max size reached."); - return result; - } - - private static JObject ToJson(StackItem item, HashSet context, ref int maxSize) + public static JObject ToJson(this StackItem item, HashSet context, ref int maxSize) { JObject json = new() { diff --git a/src/Neo/Wallets/AssetDescriptor.cs b/src/Neo/Wallets/AssetDescriptor.cs index 7b9be7b16b..e8ba27b4e4 100644 --- a/src/Neo/Wallets/AssetDescriptor.cs +++ b/src/Neo/Wallets/AssetDescriptor.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; diff --git a/src/Plugins/ApplicationLogs/ApplicationLogs.csproj b/src/Plugins/ApplicationLogs/ApplicationLogs.csproj index b0c31a7180..a878df9961 100644 --- a/src/Plugins/ApplicationLogs/ApplicationLogs.csproj +++ b/src/Plugins/ApplicationLogs/ApplicationLogs.csproj @@ -1,6 +1,6 @@ - net8.0 + net9.0 Neo.Plugins.ApplicationLogs Neo.Plugins enable diff --git a/src/Plugins/ApplicationLogs/Store/LogStorageStore.cs b/src/Plugins/ApplicationLogs/Store/LogStorageStore.cs index 147a80034b..433359e261 100644 --- a/src/Plugins/ApplicationLogs/Store/LogStorageStore.cs +++ b/src/Plugins/ApplicationLogs/Store/LogStorageStore.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Persistence; using Neo.Plugins.ApplicationLogs.Store.States; using Neo.SmartContract; diff --git a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs index d6a4340544..ef26fc4130 100644 --- a/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs +++ b/src/Plugins/DBFTPlugin/Consensus/ConsensusContext.cs @@ -11,6 +11,7 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/src/Plugins/DBFTPlugin/DBFTPlugin.csproj b/src/Plugins/DBFTPlugin/DBFTPlugin.csproj index 93c77ad1f8..84198f62e8 100644 --- a/src/Plugins/DBFTPlugin/DBFTPlugin.csproj +++ b/src/Plugins/DBFTPlugin/DBFTPlugin.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Consensus.DBFT Neo.Consensus ../../../bin/$(PackageId) diff --git a/src/Plugins/DBFTPlugin/Messages/PrepareResponse.cs b/src/Plugins/DBFTPlugin/Messages/PrepareResponse.cs index b4608ff9af..3c49693c69 100644 --- a/src/Plugins/DBFTPlugin/Messages/PrepareResponse.cs +++ b/src/Plugins/DBFTPlugin/Messages/PrepareResponse.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Plugins.DBFTPlugin.Types; using System.IO; diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs index 60e0e24e5a..d1fd548bcf 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs @@ -9,43 +9,49 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; +#nullable enable + +using System.Collections; +using System.Collections.Generic; using System.IO; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class DB : IDisposable + /// + /// A DB is a persistent ordered map from keys to values. + /// A DB is safe for concurrent access from multiple threads without any external synchronization. + /// Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is. + /// + public class DB : LevelDBHandle, IEnumerable> { - private IntPtr handle; - - /// - /// Return true if haven't got valid handle - /// - public bool IsDisposed => handle == IntPtr.Zero; - - private DB(IntPtr handle) - { - this.handle = handle; - } + private DB(nint handle) : base(handle) { } - public void Dispose() + protected override void FreeUnManagedObjects() { - if (handle != IntPtr.Zero) + if (Handle != nint.Zero) { - Native.leveldb_close(handle); - handle = IntPtr.Zero; + Native.leveldb_close(Handle); } } + /// + /// Remove the database entry (if any) for "key". + /// It is not an error if "key" did not exist in the database. + /// Note: consider setting new WriteOptions{ Sync = true }. + /// public void Delete(WriteOptions options, byte[] key) { - Native.leveldb_delete(handle, options.handle, key, (UIntPtr)key.Length, out IntPtr error); + Native.leveldb_delete(Handle, options.Handle, key, (nuint)key.Length, out var error); NativeHelper.CheckError(error); } + /// + /// If the database contains an entry for "key" return the value, + /// otherwise return null. + /// public byte[] Get(ReadOptions options, byte[] key) { - IntPtr value = Native.leveldb_get(handle, options.handle, key, (UIntPtr)key.Length, out UIntPtr length, out IntPtr error); + var value = Native.leveldb_get(Handle, options.Handle, key, (nuint)key.Length, out var length, out var error); try { NativeHelper.CheckError(error); @@ -53,16 +59,16 @@ public byte[] Get(ReadOptions options, byte[] key) } finally { - if (value != IntPtr.Zero) Native.leveldb_free(value); + if (value != nint.Zero) Native.leveldb_free(value); } } public bool Contains(ReadOptions options, byte[] key) { - IntPtr value = Native.leveldb_get(handle, options.handle, key, (UIntPtr)key.Length, out _, out IntPtr error); + var value = Native.leveldb_get(Handle, options.Handle, key, (nuint)key.Length, out _, out var error); NativeHelper.CheckError(error); - if (value != IntPtr.Zero) + if (value != nint.Zero) { Native.leveldb_free(value); return true; @@ -71,14 +77,14 @@ public bool Contains(ReadOptions options, byte[] key) return false; } - public Snapshot GetSnapshot() + public Snapshot CreateSnapshot() { - return new Snapshot(handle); + return new Snapshot(Handle); } - public Iterator NewIterator(ReadOptions options) + public Iterator CreateIterator(ReadOptions options) { - return new Iterator(Native.leveldb_create_iterator(handle, options.handle)); + return new Iterator(Native.leveldb_create_iterator(Handle, options.Handle)); } public static DB Open(string name) @@ -88,27 +94,47 @@ public static DB Open(string name) public static DB Open(string name, Options options) { - IntPtr handle = Native.leveldb_open(options.handle, Path.GetFullPath(name), out IntPtr error); + var handle = Native.leveldb_open(options.Handle, Path.GetFullPath(name), out var error); NativeHelper.CheckError(error); return new DB(handle); } + /// + /// Set the database entry for "key" to "value". + /// Note: consider setting new WriteOptions{ Sync = true }. + /// public void Put(WriteOptions options, byte[] key, byte[] value) { - Native.leveldb_put(handle, options.handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length, out IntPtr error); + Native.leveldb_put(Handle, options.Handle, key, (nuint)key.Length, value, (nuint)value.Length, out var error); NativeHelper.CheckError(error); } + /// + /// If a DB cannot be opened, you may attempt to call this method to + /// resurrect as much of the contents of the database as possible. + /// Some data may be lost, so be careful when calling this function + /// on a database that contains important information. + /// public static void Repair(string name, Options options) { - Native.leveldb_repair_db(options.handle, Path.GetFullPath(name), out IntPtr error); + Native.leveldb_repair_db(options.Handle, Path.GetFullPath(name), out var error); NativeHelper.CheckError(error); } public void Write(WriteOptions options, WriteBatch write_batch) { - Native.leveldb_write(handle, options.handle, write_batch.handle, out IntPtr error); + Native.leveldb_write(Handle, options.Handle, write_batch.Handle, out var error); NativeHelper.CheckError(error); } + + public IEnumerator> GetEnumerator() + { + using var iterator = CreateIterator(ReadOptions.Default); + for (iterator.SeekToFirst(); iterator.Valid(); iterator.Next()) + yield return new KeyValuePair(iterator.Key(), iterator.Value()); + } + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs index 2ac3a0005f..c224d23c6d 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs @@ -14,17 +14,17 @@ using System.Collections.Generic; using System.Runtime.InteropServices; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { public static class Helper { - public static IEnumerable Seek(this DB db, ReadOptions options, byte[] prefix, SeekDirection direction, Func resultSelector) + public static IEnumerable<(byte[], byte[])> Seek(this DB db, ReadOptions options, byte[] prefix, SeekDirection direction) { - using Iterator it = db.NewIterator(options); + using Iterator it = db.CreateIterator(options); if (direction == SeekDirection.Forward) { for (it.Seek(prefix); it.Valid(); it.Next()) - yield return resultSelector(it.Key(), it.Value()); + yield return new(it.Key(), it.Value()); } else { @@ -37,18 +37,7 @@ public static IEnumerable Seek(this DB db, ReadOptions options, byte[] pre it.Prev(); for (; it.Valid(); it.Prev()) - yield return resultSelector(it.Key(), it.Value()); - } - } - - public static IEnumerable FindRange(this DB db, ReadOptions options, byte[] startKey, byte[] endKey, Func resultSelector) - { - using Iterator it = db.NewIterator(options); - for (it.Seek(startKey); it.Valid(); it.Next()) - { - byte[] key = it.Key(); - if (key.AsSpan().SequenceCompareTo(endKey) > 0) break; - yield return resultSelector(key, it.Value()); + yield return new(it.Key(), it.Value()); } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs index 6c025380fb..60c1af71db 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs @@ -9,76 +9,89 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class Iterator : IDisposable + /// + /// An iterator yields a sequence of key/value pairs from a database. + /// + public class Iterator : LevelDBHandle { - private IntPtr handle; - - internal Iterator(IntPtr handle) - { - this.handle = handle; - } + internal Iterator(nint handle) : base(handle) { } private void CheckError() { - Native.leveldb_iter_get_error(handle, out IntPtr error); + Native.leveldb_iter_get_error(Handle, out var error); NativeHelper.CheckError(error); } - public void Dispose() + protected override void FreeUnManagedObjects() { - if (handle != IntPtr.Zero) + if (Handle != nint.Zero) { - Native.leveldb_iter_destroy(handle); - handle = IntPtr.Zero; + Native.leveldb_iter_destroy(Handle); } } + /// + /// Return the key for the current entry. + /// REQUIRES: Valid() + /// public byte[] Key() { - IntPtr key = Native.leveldb_iter_key(handle, out UIntPtr length); + var key = Native.leveldb_iter_key(Handle, out var length); CheckError(); return key.ToByteArray(length); } + /// + /// Moves to the next entry in the source. + /// After this call, Valid() is true if the iterator was not positioned at the last entry in the source. + /// REQUIRES: Valid() + /// public void Next() { - Native.leveldb_iter_next(handle); + Native.leveldb_iter_next(Handle); CheckError(); } public void Prev() { - Native.leveldb_iter_prev(handle); + Native.leveldb_iter_prev(Handle); CheckError(); } - public void Seek(byte[] target) + /// + /// Position at the first key in the source that at or past target + /// The iterator is Valid() after this call if the source contains + /// an entry that comes at or past target. + /// + public void Seek(byte[] key) { - Native.leveldb_iter_seek(handle, target, (UIntPtr)target.Length); + Native.leveldb_iter_seek(Handle, key, (nuint)key.Length); } public void SeekToFirst() { - Native.leveldb_iter_seek_to_first(handle); + Native.leveldb_iter_seek_to_first(Handle); } + /// + /// Position at the last key in the source. + /// The iterator is Valid() after this call if the source is not empty. + /// public void SeekToLast() { - Native.leveldb_iter_seek_to_last(handle); + Native.leveldb_iter_seek_to_last(Handle); } public bool Valid() { - return Native.leveldb_iter_valid(handle); + return Native.leveldb_iter_valid(Handle); } public byte[] Value() { - IntPtr value = Native.leveldb_iter_value(handle, out UIntPtr length); + var value = Native.leveldb_iter_value(Handle, out var length); CheckError(); return value.ToByteArray(length); } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs index c9cca42070..ba900a655e 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs @@ -11,7 +11,7 @@ using System.Data.Common; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { public class LevelDBException : DbException { diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs new file mode 100644 index 0000000000..6868eae884 --- /dev/null +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// LevelDBHandle.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 System; + +namespace Neo.IO.Storage.LevelDB +{ + /// + /// Base class for all LevelDB objects + /// + public abstract class LevelDBHandle(nint handle) : IDisposable + { + private bool _disposed = false; + + public nint Handle { get; private set; } = handle; + + /// + /// Return true if haven't got valid handle + /// + public bool IsDisposed => _disposed || Handle == IntPtr.Zero; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected abstract void FreeUnManagedObjects(); + + void Dispose(bool disposing) + { + if (!_disposed) + { + _disposed = true; + if (Handle != nint.Zero) + { + FreeUnManagedObjects(); + Handle = nint.Zero; + } + } + } + + ~LevelDBHandle() + { + Dispose(false); + } + } +} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs index acf8fa82b9..04bffbd997 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs @@ -13,68 +13,68 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { public enum CompressionType : byte { - kNoCompression = 0x0, - kSnappyCompression = 0x1 + NoCompression = 0x0, + SnappyCompression = 0x1 } public static class Native { #region Logger [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger); + public static extern nint leveldb_logger_create(nint /* Action */ logger); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_logger_destroy(IntPtr /* logger*/ option); + public static extern void leveldb_logger_destroy(nint /* logger*/ option); #endregion #region DB [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_open(IntPtr /* Options*/ options, string name, out IntPtr error); + public static extern nint leveldb_open(nint /* Options*/ options, string name, out nint error); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_close(IntPtr /*DB */ db); + public static extern void leveldb_close(nint /*DB */ db); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_put(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen, out IntPtr errptr); + public static extern void leveldb_put(nint /* DB */ db, nint /* WriteOptions*/ options, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen, out nint errptr); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_delete(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, out IntPtr errptr); + public static extern void leveldb_delete(nint /* DB */ db, nint /* WriteOptions*/ options, byte[] key, UIntPtr keylen, out nint errptr); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_write(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, IntPtr /* WriteBatch */ batch, out IntPtr errptr); + public static extern void leveldb_write(nint /* DB */ db, nint /* WriteOptions*/ options, nint /* WriteBatch */ batch, out nint errptr); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, byte[] key, UIntPtr keylen, out UIntPtr vallen, out IntPtr errptr); + public static extern nint leveldb_get(nint /* DB */ db, nint /* ReadOptions*/ options, byte[] key, UIntPtr keylen, out UIntPtr vallen, out nint errptr); //[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - //static extern void leveldb_approximate_sizes(IntPtr /* DB */ db, int num_ranges, byte[] range_start_key, long range_start_key_len, byte[] range_limit_key, long range_limit_key_len, out long sizes); + //static extern void leveldb_approximate_sizes(nint /* DB */ db, int num_ranges, byte[] range_start_key, long range_start_key_len, byte[] range_limit_key, long range_limit_key_len, out long sizes); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_iterator(IntPtr /* DB */ db, IntPtr /* ReadOption */ options); + public static extern nint leveldb_create_iterator(nint /* DB */ db, nint /* ReadOption */ options); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_snapshot(IntPtr /* DB */ db); + public static extern nint leveldb_create_snapshot(nint /* DB */ db); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_release_snapshot(IntPtr /* DB */ db, IntPtr /* SnapShot*/ snapshot); + public static extern void leveldb_release_snapshot(nint /* DB */ db, nint /* SnapShot*/ snapshot); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_property_value(IntPtr /* DB */ db, string propname); + public static extern nint leveldb_property_value(nint /* DB */ db, string propname); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_repair_db(IntPtr /* Options*/ options, string name, out IntPtr error); + public static extern void leveldb_repair_db(nint /* Options*/ options, string name, out nint error); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_destroy_db(IntPtr /* Options*/ options, string name, out IntPtr error); + public static extern void leveldb_destroy_db(nint /* Options*/ options, string name, out nint error); - #region extensions + #region extensions [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_free(IntPtr /* void */ ptr); + public static extern void leveldb_free(nint /* void */ ptr); #endregion @@ -83,167 +83,167 @@ public static class Native #region Env [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_default_env(); + public static extern nint leveldb_create_default_env(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_env_destroy(IntPtr /*Env*/ cache); + public static extern void leveldb_env_destroy(nint /*Env*/ cache); #endregion #region Iterator [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_destroy(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_destroy(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] - public static extern bool leveldb_iter_valid(IntPtr /*Iterator*/ iterator); + public static extern bool leveldb_iter_valid(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek_to_first(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_seek_to_first(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek_to_last(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_seek_to_last(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, byte[] key, UIntPtr length); + public static extern void leveldb_iter_seek(nint /*Iterator*/ iterator, byte[] key, UIntPtr length); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_next(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_next(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_prev(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_prev(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_iter_key(IntPtr /*Iterator*/ iterator, out UIntPtr length); + public static extern nint leveldb_iter_key(nint /*Iterator*/ iterator, out UIntPtr length); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_iter_value(IntPtr /*Iterator*/ iterator, out UIntPtr length); + public static extern nint leveldb_iter_value(nint /*Iterator*/ iterator, out UIntPtr length); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_get_error(IntPtr /*Iterator*/ iterator, out IntPtr error); + public static extern void leveldb_iter_get_error(nint /*Iterator*/ iterator, out nint error); #endregion #region Options [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_options_create(); + public static extern nint leveldb_options_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_destroy(IntPtr /*Options*/ options); + public static extern void leveldb_options_destroy(nint /*Options*/ options); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_create_if_missing(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); + public static extern void leveldb_options_set_create_if_missing(nint /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_error_if_exists(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); + public static extern void leveldb_options_set_error_if_exists(nint /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_info_log(IntPtr /*Options*/ options, IntPtr /* Logger */ logger); + public static extern void leveldb_options_set_info_log(nint /*Options*/ options, nint /* Logger */ logger); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_paranoid_checks(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); + public static extern void leveldb_options_set_paranoid_checks(nint /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_env(IntPtr /*Options*/ options, IntPtr /*Env*/ env); + public static extern void leveldb_options_set_env(nint /*Options*/ options, nint /*Env*/ env); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_write_buffer_size(IntPtr /*Options*/ options, UIntPtr size); + public static extern void leveldb_options_set_write_buffer_size(nint /*Options*/ options, UIntPtr size); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_max_open_files(IntPtr /*Options*/ options, int max); + public static extern void leveldb_options_set_max_open_files(nint /*Options*/ options, int max); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_cache(IntPtr /*Options*/ options, IntPtr /*Cache*/ cache); + public static extern void leveldb_options_set_cache(nint /*Options*/ options, nint /*Cache*/ cache); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_block_size(IntPtr /*Options*/ options, UIntPtr size); + public static extern void leveldb_options_set_block_size(nint /*Options*/ options, UIntPtr size); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_block_restart_interval(IntPtr /*Options*/ options, int interval); + public static extern void leveldb_options_set_block_restart_interval(nint /*Options*/ options, int interval); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_compression(IntPtr /*Options*/ options, CompressionType level); + public static extern void leveldb_options_set_compression(nint /*Options*/ options, CompressionType level); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_comparator(IntPtr /*Options*/ options, IntPtr /*Comparator*/ comparer); + public static extern void leveldb_options_set_comparator(nint /*Options*/ options, nint /*Comparator*/ comparer); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_filter_policy(IntPtr /*Options*/ options, IntPtr /*FilterPolicy*/ policy); + public static extern void leveldb_options_set_filter_policy(nint /*Options*/ options, nint /*FilterPolicy*/ policy); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_filterpolicy_create_bloom(int bits_per_key); + public static extern nint leveldb_filterpolicy_create_bloom(int bits_per_key); #endregion #region ReadOptions [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_readoptions_create(); + public static extern nint leveldb_readoptions_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_destroy(IntPtr /*ReadOptions*/ options); + public static extern void leveldb_readoptions_destroy(nint /*ReadOptions*/ options); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_verify_checksums(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); + public static extern void leveldb_readoptions_set_verify_checksums(nint /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_fill_cache(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); + public static extern void leveldb_readoptions_set_fill_cache(nint /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_snapshot(IntPtr /*ReadOptions*/ options, IntPtr /*SnapShot*/ snapshot); + public static extern void leveldb_readoptions_set_snapshot(nint /*ReadOptions*/ options, nint /*SnapShot*/ snapshot); #endregion #region WriteBatch [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_writebatch_create(); + public static extern nint leveldb_writebatch_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_destroy(IntPtr /* WriteBatch */ batch); + public static extern void leveldb_writebatch_destroy(nint /* WriteBatch */ batch); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_clear(IntPtr /* WriteBatch */ batch); + public static extern void leveldb_writebatch_clear(nint /* WriteBatch */ batch); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_put(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen); + public static extern void leveldb_writebatch_put(nint /* WriteBatch */ batch, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_delete(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen); + public static extern void leveldb_writebatch_delete(nint /* WriteBatch */ batch, byte[] key, UIntPtr keylen); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_iterate(IntPtr /* WriteBatch */ batch, object state, Action put, Action deleted); + public static extern void leveldb_writebatch_iterate(nint /* WriteBatch */ batch, object state, Action put, Action deleted); #endregion #region WriteOptions [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_writeoptions_create(); + public static extern nint leveldb_writeoptions_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_destroy(IntPtr /*WriteOptions*/ options); + public static extern void leveldb_writeoptions_destroy(nint /*WriteOptions*/ options); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_set_sync(IntPtr /*WriteOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); + public static extern void leveldb_writeoptions_set_sync(nint /*WriteOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); #endregion - #region Cache + #region Cache [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_cache_create_lru(int capacity); + public static extern nint leveldb_cache_create_lru(int capacity); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_cache_destroy(IntPtr /*Cache*/ cache); + public static extern void leveldb_cache_destroy(nint /*Cache*/ cache); #endregion #region Comparator [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr /* leveldb_comparator_t* */ + public static extern nint /* leveldb_comparator_t* */ leveldb_comparator_create( - IntPtr /* void* */ state, - IntPtr /* void (*)(void*) */ destructor, - IntPtr + nint /* void* */ state, + nint /* void (*)(void*) */ destructor, + nint /* int (*compare)(void*, const char* a, size_t alen, const char* b, size_t blen) */ compare, - IntPtr /* const char* (*)(void*) */ name); + nint /* const char* (*)(void*) */ name); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_comparator_destroy(IntPtr /* leveldb_comparator_t* */ cmp); + public static extern void leveldb_comparator_destroy(nint /* leveldb_comparator_t* */ cmp); #endregion } @@ -251,9 +251,9 @@ public static extern IntPtr /* leveldb_comparator_t* */ internal static class NativeHelper { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CheckError(IntPtr error) + public static void CheckError(nint error) { - if (error != IntPtr.Zero) + if (error != nint.Zero) { string message = Marshal.PtrToStringAnsi(error); Native.leveldb_free(error); diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs index 989987eeed..b6c80422a3 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs @@ -11,88 +11,134 @@ using System; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class Options + /// + /// Options to control the behavior of a database (passed to Open) + /// + /// the setter methods for InfoLogger, Env, and Cache only "safe to clean up guarantee". Do not + /// use Option object if throws. + /// + public class Options : LevelDBHandle { - public static readonly Options Default = new Options(); - internal readonly IntPtr handle = Native.leveldb_options_create(); + public static readonly Options Default = new(); + public Options() : base(Native.leveldb_options_create()) { } + + /// + /// If true, the database will be created if it is missing. + /// public bool CreateIfMissing { - set - { - Native.leveldb_options_set_create_if_missing(handle, value); - } + set { Native.leveldb_options_set_create_if_missing(Handle, value); } } + /// + /// If true, an error is raised if the database already exists. + /// public bool ErrorIfExists { - set - { - Native.leveldb_options_set_error_if_exists(handle, value); - } + set { Native.leveldb_options_set_error_if_exists(Handle, value); } } + /// + /// If true, the implementation will do aggressive checking of the + /// data it is processing and will stop early if it detects any + /// errors. This may have unforeseen ramifications: for example, a + /// corruption of one DB entry may cause a large number of entries to + /// become unreadable or for the entire DB to become unopenable. + /// public bool ParanoidChecks { - set - { - Native.leveldb_options_set_paranoid_checks(handle, value); - } + set { Native.leveldb_options_set_paranoid_checks(Handle, value); } } + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + + /// + /// Amount of data to build up in memory (backed by an unsorted log + /// on disk) before converting to a sorted on-disk file. + /// + /// Larger values increase performance, especially during bulk loads. + /// Up to two write buffers may be held in memory at the same time, + /// so you may wish to adjust this parameter to control memory usage. + /// Also, a larger write buffer will result in a longer recovery time + /// the next time the database is opened. + /// + /// Default: 4MB + /// public int WriteBufferSize { - set - { - Native.leveldb_options_set_write_buffer_size(handle, (UIntPtr)value); - } + set { Native.leveldb_options_set_write_buffer_size(Handle, (UIntPtr)value); } } + /// + /// Number of open files that can be used by the DB. You may need to + /// increase this if your database has a large working set (budget + /// one open file per 2MB of working set). + /// + /// Default: 1000 + /// public int MaxOpenFiles { - set - { - Native.leveldb_options_set_max_open_files(handle, value); - } + set { Native.leveldb_options_set_max_open_files(Handle, value); } } + /// + /// Approximate size of user data packed per block. Note that the + /// block size specified here corresponds to uncompressed data. The + /// actual size of the unit read from disk may be smaller if + /// compression is enabled. This parameter can be changed dynamically. + /// + /// Default: 4K + /// public int BlockSize { - set - { - Native.leveldb_options_set_block_size(handle, (UIntPtr)value); - } + set { Native.leveldb_options_set_block_size(Handle, (UIntPtr)value); } } + /// + /// Number of keys between restart points for delta encoding of keys. + /// This parameter can be changed dynamically. + /// Most clients should leave this parameter alone. + /// + /// Default: 16 + /// public int BlockRestartInterval { - set - { - Native.leveldb_options_set_block_restart_interval(handle, value); - } + set { Native.leveldb_options_set_block_restart_interval(Handle, value); } } - public CompressionType Compression + /// + /// Compress blocks using the specified compression algorithm. + /// This parameter can be changed dynamically. + /// + /// Default: kSnappyCompression, which gives lightweight but fast compression. + /// + /// Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + /// ~200-500MB/s compression + /// ~400-800MB/s decompression + /// Note that these speeds are significantly faster than most + /// persistent storage speeds, and therefore it is typically never + /// worth switching to kNoCompression. Even if the input data is + /// incompressible, the kSnappyCompression implementation will + /// efficiently detect that and will switch to uncompressed mode. + /// + public CompressionType CompressionLevel { - set - { - Native.leveldb_options_set_compression(handle, value); - } + set { Native.leveldb_options_set_compression(Handle, value); } } - public IntPtr FilterPolicy + public nint FilterPolicy { - set - { - Native.leveldb_options_set_filter_policy(handle, value); - } + set { Native.leveldb_options_set_filter_policy(Handle, value); } } - ~Options() + protected override void FreeUnManagedObjects() { - Native.leveldb_options_destroy(handle); + Native.leveldb_options_destroy(Handle); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs index 727ae9f02a..2a219fa1df 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs @@ -9,42 +9,51 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class ReadOptions + /// + /// Options that control read operations. + /// + public class ReadOptions : LevelDBHandle { - public static readonly ReadOptions Default = new ReadOptions(); - internal readonly IntPtr handle = Native.leveldb_readoptions_create(); + public static readonly ReadOptions Default = new(); + + public ReadOptions() : base(Native.leveldb_readoptions_create()) { } + /// + /// If true, all data read from underlying storage will be + /// verified against corresponding checksums. + /// public bool VerifyChecksums { - set - { - Native.leveldb_readoptions_set_verify_checksums(handle, value); - } + set { Native.leveldb_readoptions_set_verify_checksums(Handle, value); } } + /// + /// Should the data read for this iteration be cached in memory? + /// Callers may wish to set this field to false for bulk scans. + /// Default: true + /// public bool FillCache { - set - { - Native.leveldb_readoptions_set_fill_cache(handle, value); - } + set { Native.leveldb_readoptions_set_fill_cache(Handle, value); } } + /// + /// If "snapshot" is provided, read as of the supplied snapshot + /// (which must belong to the DB that is being read and which must + /// not have been released). + /// If "snapshot" is not set, use an implicit + /// snapshot of the state at the beginning of this read operation. + /// public Snapshot Snapshot { - set - { - Native.leveldb_readoptions_set_snapshot(handle, value.handle); - } + set { Native.leveldb_readoptions_set_snapshot(Handle, value.Handle); } } - ~ReadOptions() + protected override void FreeUnManagedObjects() { - Native.leveldb_readoptions_destroy(handle); + Native.leveldb_readoptions_destroy(Handle); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs index 14280fbc8f..400535e971 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs @@ -9,26 +9,26 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class Snapshot : IDisposable + /// + /// A Snapshot is an immutable object and can therefore be safely + /// accessed from multiple threads without any external synchronization. + /// + public class Snapshot : LevelDBHandle { - internal IntPtr db, handle; + internal nint _db; - internal Snapshot(IntPtr db) + internal Snapshot(nint db) : base(Native.leveldb_create_snapshot(db)) { - this.db = db; - handle = Native.leveldb_create_snapshot(db); + _db = db; } - public void Dispose() + protected override void FreeUnManagedObjects() { - if (handle != IntPtr.Zero) + if (Handle != nint.Zero) { - Native.leveldb_release_snapshot(db, handle); - handle = IntPtr.Zero; + Native.leveldb_release_snapshot(_db, Handle); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs index ad82dad450..300476a5f1 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs @@ -11,30 +11,52 @@ using System; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class WriteBatch + /// + /// WriteBatch holds a collection of updates to apply atomically to a DB. + /// + /// The updates are applied in the order in which they are added + /// to the WriteBatch. For example, the value of "key" will be "v3" + /// after the following batch is written: + /// + /// batch.Put("key", "v1"); + /// batch.Delete("key"); + /// batch.Put("key", "v2"); + /// batch.Put("key", "v3"); + /// + public class WriteBatch : LevelDBHandle { - internal readonly IntPtr handle = Native.leveldb_writebatch_create(); + public WriteBatch() : base(Native.leveldb_writebatch_create()) { } - ~WriteBatch() + /// + /// Clear all updates buffered in this batch. + /// + public void Clear() { - Native.leveldb_writebatch_destroy(handle); + Native.leveldb_writebatch_clear(Handle); } - public void Clear() + /// + /// Store the mapping "key->value" in the database. + /// + public void Put(byte[] key, byte[] value) { - Native.leveldb_writebatch_clear(handle); + Native.leveldb_writebatch_put(Handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); } + /// + /// If the database contains a mapping for "key", erase it. + /// Else do nothing. + /// public void Delete(byte[] key) { - Native.leveldb_writebatch_delete(handle, key, (UIntPtr)key.Length); + Native.leveldb_writebatch_delete(Handle, key, (UIntPtr)key.Length); } - public void Put(byte[] key, byte[] value) + protected override void FreeUnManagedObjects() { - Native.leveldb_writebatch_put(handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); + Native.leveldb_writebatch_destroy(Handle); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs index 48915ba480..a4a304ad33 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs @@ -9,28 +9,42 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class WriteOptions + /// + /// Options that control write operations. + /// + public class WriteOptions : LevelDBHandle { - public static readonly WriteOptions Default = new WriteOptions(); - public static readonly WriteOptions SyncWrite = new WriteOptions { Sync = true }; + public static readonly WriteOptions Default = new(); + public static readonly WriteOptions SyncWrite = new() { Sync = true }; - internal readonly IntPtr handle = Native.leveldb_writeoptions_create(); + public WriteOptions() : base(Native.leveldb_writeoptions_create()) { } + /// + /// If true, the write will be flushed from the operating system + /// buffer cache (by calling WritableFile::Sync()) before the write + /// is considered complete. If this flag is true, writes will be + /// slower. + /// + /// If this flag is false, and the machine crashes, some recent + /// writes may be lost. Note that if it is just the process that + /// crashes (i.e., the machine does not reboot), no writes will be + /// lost even if sync==false. + /// + /// In other words, a DB write with sync==false has similar + /// crash semantics as the "write()" system call. A DB write + /// with sync==true has similar crash semantics to a "write()" + /// system call followed by "fsync()". + /// public bool Sync { - set - { - Native.leveldb_writeoptions_set_sync(handle, value); - } + set { Native.leveldb_writeoptions_set_sync(Handle, value); } } - ~WriteOptions() + protected override void FreeUnManagedObjects() { - Native.leveldb_writeoptions_destroy(handle); + Native.leveldb_writeoptions_destroy(Handle); } } } diff --git a/src/Plugins/LevelDBStore/LevelDBStore.csproj b/src/Plugins/LevelDBStore/LevelDBStore.csproj index 23cf469640..09e4abb7bd 100644 --- a/src/Plugins/LevelDBStore/LevelDBStore.csproj +++ b/src/Plugins/LevelDBStore/LevelDBStore.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Plugins.Storage.LevelDBStore Neo.Plugins.Storage true diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs b/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs index 9c676e8a7f..6f39abd6af 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Data.LevelDB; +using Neo.IO.Storage.LevelDB; using Neo.Persistence; using System; using System.Linq; diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index f66164285b..d9f6772a0b 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -9,67 +9,86 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Data.LevelDB; +using Neo.IO.Storage.LevelDB; using Neo.Persistence; +using System.Collections; using System.Collections.Generic; -using LSnapshot = Neo.IO.Data.LevelDB.Snapshot; +using LSnapshot = Neo.IO.Storage.LevelDB.Snapshot; namespace Neo.Plugins.Storage { - internal class Snapshot : ISnapshot + /// + /// Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is. + /// + internal class Snapshot : ISnapshot, IEnumerable> { - private readonly DB db; - private readonly LSnapshot snapshot; - private readonly ReadOptions options; - private readonly WriteBatch batch; + private readonly DB _db; + private readonly LSnapshot _snapshot; + private readonly ReadOptions _readOptions; + private readonly WriteBatch _batch; + private readonly object _lock = new(); public Snapshot(DB db) { - this.db = db; - snapshot = db.GetSnapshot(); - options = new ReadOptions { FillCache = false, Snapshot = snapshot }; - batch = new WriteBatch(); + _db = db; + _snapshot = db.CreateSnapshot(); + _readOptions = new ReadOptions { FillCache = false, Snapshot = _snapshot }; + _batch = new WriteBatch(); } public void Commit() { - db.Write(WriteOptions.Default, batch); + lock (_lock) + _db.Write(WriteOptions.Default, _batch); } public void Delete(byte[] key) { - batch.Delete(key); + lock (_lock) + _batch.Delete(key); } public void Dispose() { - snapshot.Dispose(); + _snapshot.Dispose(); + _readOptions.Dispose(); } public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) { - return db.Seek(options, prefix, direction, (k, v) => (k, v)); + return _db.Seek(_readOptions, prefix, direction); } public void Put(byte[] key, byte[] value) { - batch.Put(key, value); + lock (_lock) + _batch.Put(key, value); } public bool Contains(byte[] key) { - return db.Contains(options, key); + return _db.Contains(_readOptions, key); } public byte[] TryGet(byte[] key) { - return db.Get(options, key); + return _db.Get(_readOptions, key); } public bool TryGet(byte[] key, out byte[] value) { - value = db.Get(options, key); + value = _db.Get(_readOptions, key); return value != null; } + + public IEnumerator> GetEnumerator() + { + using var iterator = _db.CreateIterator(_readOptions); + for (iterator.SeekToFirst(); iterator.Valid(); iterator.Next()) + yield return new KeyValuePair(iterator.Key(), iterator.Value()); + } + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs index 175b71a8fe..0a0f630613 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -9,65 +9,71 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Data.LevelDB; +using Neo.IO.Storage.LevelDB; using Neo.Persistence; +using System.Collections; using System.Collections.Generic; namespace Neo.Plugins.Storage { - internal class Store : IStore + /// + /// Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is. + /// + internal class Store : IStore, IEnumerable> { - private readonly DB db; + private readonly DB _db; + private readonly Options _options; public Store(string path) { - db = DB.Open(path, new Options { CreateIfMissing = true, FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15) }); + _options = new Options + { + CreateIfMissing = true, + FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15), + CompressionLevel = CompressionType.SnappyCompression, + }; + _db = DB.Open(path, _options); } public void Delete(byte[] key) { - db.Delete(WriteOptions.Default, key); + _db.Delete(WriteOptions.Default, key); } public void Dispose() { - db.Dispose(); + _db.Dispose(); + _options.Dispose(); } - public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) - { - return db.Seek(ReadOptions.Default, prefix, direction, (k, v) => (k, v)); - } - - public ISnapshot GetSnapshot() - { - return new Snapshot(db); - } + public ISnapshot GetSnapshot() => + new Snapshot(_db); - public void Put(byte[] key, byte[] value) - { - db.Put(WriteOptions.Default, key, value); - } + public void Put(byte[] key, byte[] value) => + _db.Put(WriteOptions.Default, key, value); - public void PutSync(byte[] key, byte[] value) - { - db.Put(WriteOptions.SyncWrite, key, value); - } + public void PutSync(byte[] key, byte[] value) => + _db.Put(WriteOptions.SyncWrite, key, value); - public bool Contains(byte[] key) - { - return db.Contains(ReadOptions.Default, key); - } + public bool Contains(byte[] key) => + _db.Contains(ReadOptions.Default, key); - public byte[] TryGet(byte[] key) - { - return db.Get(ReadOptions.Default, key); - } + public byte[] TryGet(byte[] key) => + _db.Get(ReadOptions.Default, key); public bool TryGet(byte[] key, out byte[] value) { - value = db.Get(ReadOptions.Default, key); + value = _db.Get(ReadOptions.Default, key); return value != null; } + + public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) => + _db.Seek(ReadOptions.Default, prefix, direction); + + public IEnumerator> GetEnumerator() => + _db.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); } } diff --git a/src/Plugins/MPTTrie/Cryptography/MPTTrie/Cache.cs b/src/Plugins/MPTTrie/Cryptography/MPTTrie/Cache.cs index d8baef8529..e832e8994b 100644 --- a/src/Plugins/MPTTrie/Cryptography/MPTTrie/Cache.cs +++ b/src/Plugins/MPTTrie/Cryptography/MPTTrie/Cache.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Persistence; using System.Collections.Generic; using System.IO; diff --git a/src/Plugins/MPTTrie/Cryptography/MPTTrie/Node.Hash.cs b/src/Plugins/MPTTrie/Cryptography/MPTTrie/Node.Hash.cs index e0190dd146..dc58535fb3 100644 --- a/src/Plugins/MPTTrie/Cryptography/MPTTrie/Node.Hash.cs +++ b/src/Plugins/MPTTrie/Cryptography/MPTTrie/Node.Hash.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System; using System.IO; diff --git a/src/Plugins/MPTTrie/Cryptography/MPTTrie/Trie.Proof.cs b/src/Plugins/MPTTrie/Cryptography/MPTTrie/Trie.Proof.cs index d3c04053b9..895f666d08 100644 --- a/src/Plugins/MPTTrie/Cryptography/MPTTrie/Trie.Proof.cs +++ b/src/Plugins/MPTTrie/Cryptography/MPTTrie/Trie.Proof.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO; +using Neo.Extensions; using Neo.Persistence; using System; using System.Collections.Generic; diff --git a/src/Plugins/MPTTrie/IO/ByteArrayEqualityComparer.cs b/src/Plugins/MPTTrie/IO/ByteArrayEqualityComparer.cs deleted file mode 100644 index 590306d560..0000000000 --- a/src/Plugins/MPTTrie/IO/ByteArrayEqualityComparer.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// ByteArrayEqualityComparer.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 System.Collections.Generic; - -namespace Neo.IO -{ - public class ByteArrayEqualityComparer : IEqualityComparer - { - public static readonly ByteArrayEqualityComparer Default = new ByteArrayEqualityComparer(); - - public unsafe bool Equals(byte[] x, byte[] y) - { - if (ReferenceEquals(x, y)) return true; - if (x is null || y is null) return false; - int len = x.Length; - if (len != y.Length) return false; - if (len == 0) return true; - fixed (byte* xp = x, yp = y) - { - long* xlp = (long*)xp, ylp = (long*)yp; - for (; len >= 8; len -= 8) - { - if (*xlp != *ylp) return false; - xlp++; - ylp++; - } - byte* xbp = (byte*)xlp, ybp = (byte*)ylp; - for (; len > 0; len--) - { - if (*xbp != *ybp) return false; - xbp++; - ybp++; - } - } - return true; - } - - public int GetHashCode(byte[] obj) - { - unchecked - { - int hash = 17; - foreach (byte element in obj) - hash = hash * 31 + element; - return hash; - } - } - } -} diff --git a/src/Plugins/MPTTrie/MPTTrie.csproj b/src/Plugins/MPTTrie/MPTTrie.csproj index 3134f7ae5b..430f9328e6 100644 --- a/src/Plugins/MPTTrie/MPTTrie.csproj +++ b/src/Plugins/MPTTrie/MPTTrie.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Cryptography.MPT Neo.Cryptography true diff --git a/src/Plugins/OracleService/OracleService.csproj b/src/Plugins/OracleService/OracleService.csproj index c77ddb75d8..4f2b25cb07 100644 --- a/src/Plugins/OracleService/OracleService.csproj +++ b/src/Plugins/OracleService/OracleService.csproj @@ -1,13 +1,14 @@ - net8.0 + net9.0 Neo.Plugins.OracleService ../../../bin/$(PackageId) + diff --git a/src/Plugins/RocksDBStore/RocksDBStore.csproj b/src/Plugins/RocksDBStore/RocksDBStore.csproj index 90ed2e841a..7a827d382e 100644 --- a/src/Plugins/RocksDBStore/RocksDBStore.csproj +++ b/src/Plugins/RocksDBStore/RocksDBStore.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Plugins.Storage.RocksDBStore Neo.Plugins.Storage ../../../bin/$(PackageId) diff --git a/src/Plugins/RpcClient/ContractClient.cs b/src/Plugins/RpcClient/ContractClient.cs index f4ec020abc..02b8edc5ef 100644 --- a/src/Plugins/RpcClient/ContractClient.cs +++ b/src/Plugins/RpcClient/ContractClient.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; using Neo.SmartContract; diff --git a/src/Plugins/RpcClient/Nep17API.cs b/src/Plugins/RpcClient/Nep17API.cs index 0870670231..c1f7324c4c 100644 --- a/src/Plugins/RpcClient/Nep17API.cs +++ b/src/Plugins/RpcClient/Nep17API.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; using Neo.SmartContract; diff --git a/src/Plugins/RpcClient/RpcClient.csproj b/src/Plugins/RpcClient/RpcClient.csproj index bc6161e3cb..a8a4b84d6d 100644 --- a/src/Plugins/RpcClient/RpcClient.csproj +++ b/src/Plugins/RpcClient/RpcClient.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Network.RPC.RpcClient Neo.Network.RPC ../../../bin/$(PackageId) diff --git a/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs b/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs index fe35d9c3e5..6108a2bd55 100644 --- a/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs +++ b/src/Plugins/RpcServer/Model/BlockHashOrIndex.cs @@ -11,51 +11,52 @@ using System.Diagnostics.CodeAnalysis; -namespace Neo.Plugins.RpcServer.Model; - -public class BlockHashOrIndex +namespace Neo.Plugins.RpcServer.Model { - private readonly object _value; - - public BlockHashOrIndex(uint index) - { - _value = index; - } - - public BlockHashOrIndex(UInt256 hash) + public class BlockHashOrIndex { - _value = hash; - } + private readonly object _value; - public bool IsIndex => _value is uint; + public BlockHashOrIndex(uint index) + { + _value = index; + } - public static bool TryParse(string value, [NotNullWhen(true)] out BlockHashOrIndex blockHashOrIndex) - { - if (uint.TryParse(value, out var index)) + public BlockHashOrIndex(UInt256 hash) { - blockHashOrIndex = new BlockHashOrIndex(index); - return true; + _value = hash; } - if (UInt256.TryParse(value, out var hash)) + + public bool IsIndex => _value is uint; + + public static bool TryParse(string value, [NotNullWhen(true)] out BlockHashOrIndex blockHashOrIndex) { - blockHashOrIndex = new BlockHashOrIndex(hash); - return true; + if (uint.TryParse(value, out var index)) + { + blockHashOrIndex = new BlockHashOrIndex(index); + return true; + } + if (UInt256.TryParse(value, out var hash)) + { + blockHashOrIndex = new BlockHashOrIndex(hash); + return true; + } + blockHashOrIndex = null; + return false; } - blockHashOrIndex = null; - return false; - } - public uint AsIndex() - { - if (_value is uint intValue) - return intValue; - throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid block index")); - } + public uint AsIndex() + { + if (_value is uint intValue) + return intValue; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid block index")); + } - public UInt256 AsHash() - { - if (_value is UInt256 hash) - return hash; - throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid block hash")); + public UInt256 AsHash() + { + if (_value is UInt256 hash) + return hash; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid block hash")); + } } } diff --git a/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs b/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs index f1b7c2c92e..636522a9ae 100644 --- a/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs +++ b/src/Plugins/RpcServer/Model/ContractNameOrHashOrId.cs @@ -11,71 +11,72 @@ using System.Diagnostics.CodeAnalysis; -namespace Neo.Plugins.RpcServer.Model; - -public class ContractNameOrHashOrId +namespace Neo.Plugins.RpcServer.Model { - private readonly object _value; - - public ContractNameOrHashOrId(int id) - { - _value = id; - } - - public ContractNameOrHashOrId(UInt160 hash) - { - _value = hash; - } - - public ContractNameOrHashOrId(string name) + public class ContractNameOrHashOrId { - _value = name; - } + private readonly object _value; - public bool IsId => _value is int; - public bool IsHash => _value is UInt160; - public bool IsName => _value is string; + public ContractNameOrHashOrId(int id) + { + _value = id; + } - public static bool TryParse(string value, [NotNullWhen(true)] out ContractNameOrHashOrId contractNameOrHashOrId) - { - if (int.TryParse(value, out var id)) + public ContractNameOrHashOrId(UInt160 hash) { - contractNameOrHashOrId = new ContractNameOrHashOrId(id); - return true; + _value = hash; } - if (UInt160.TryParse(value, out var hash)) + + public ContractNameOrHashOrId(string name) { - contractNameOrHashOrId = new ContractNameOrHashOrId(hash); - return true; + _value = name; } - if (value.Length > 0) + public bool IsId => _value is int; + public bool IsHash => _value is UInt160; + public bool IsName => _value is string; + + public static bool TryParse(string value, [NotNullWhen(true)] out ContractNameOrHashOrId contractNameOrHashOrId) { - contractNameOrHashOrId = new ContractNameOrHashOrId(value); - return true; + if (int.TryParse(value, out var id)) + { + contractNameOrHashOrId = new ContractNameOrHashOrId(id); + return true; + } + if (UInt160.TryParse(value, out var hash)) + { + contractNameOrHashOrId = new ContractNameOrHashOrId(hash); + return true; + } + + if (value.Length > 0) + { + contractNameOrHashOrId = new ContractNameOrHashOrId(value); + return true; + } + contractNameOrHashOrId = null; + return false; } - contractNameOrHashOrId = null; - return false; - } - public int AsId() - { - if (_value is int intValue) - return intValue; - throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract id")); - } + public int AsId() + { + if (_value is int intValue) + return intValue; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract id")); + } - public UInt160 AsHash() - { - if (_value is UInt160 hash) - return hash; - throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract hash")); - } + public UInt160 AsHash() + { + if (_value is UInt160 hash) + return hash; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract hash")); + } - public string AsName() - { - if (_value is string name) - return name; - throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract name")); + public string AsName() + { + if (_value is string name) + return name; + throw new RpcException(RpcError.InvalidParams.WithData($"Value {_value} is not a valid contract name")); + } } } diff --git a/src/Plugins/RpcServer/ParameterConverter.cs b/src/Plugins/RpcServer/ParameterConverter.cs index 7d9b754f7c..826553c9ab 100644 --- a/src/Plugins/RpcServer/ParameterConverter.cs +++ b/src/Plugins/RpcServer/ParameterConverter.cs @@ -16,134 +16,135 @@ using System.Collections.Generic; using JToken = Neo.Json.JToken; -namespace Neo.Plugins.RpcServer; - -public static class ParameterConverter +namespace Neo.Plugins.RpcServer { - private static readonly Dictionary> s_conversionStrategies; - - static ParameterConverter() + public static class ParameterConverter { - s_conversionStrategies = new Dictionary> - { - { typeof(string), token => Result.Ok_Or(token.AsString, CreateInvalidParamError(token)) }, - { typeof(byte), ConvertNumeric }, - { typeof(sbyte), ConvertNumeric }, - { typeof(short), ConvertNumeric }, - { typeof(ushort), ConvertNumeric }, - { typeof(int), ConvertNumeric }, - { typeof(uint), ConvertNumeric }, - { typeof(long), ConvertNumeric }, - { typeof(ulong), ConvertNumeric }, - { typeof(double), token => Result.Ok_Or(token.AsNumber, CreateInvalidParamError(token)) }, - { typeof(bool), token => Result.Ok_Or(token.AsBoolean, CreateInvalidParamError(token)) }, - { typeof(UInt256), ConvertUInt256 }, - { typeof(ContractNameOrHashOrId), ConvertContractNameOrHashOrId }, - { typeof(BlockHashOrIndex), ConvertBlockHashOrIndex } - }; - } - - internal static object ConvertParameter(JToken token, Type targetType) - { - if (s_conversionStrategies.TryGetValue(targetType, out var conversionStrategy)) - return conversionStrategy(token); - throw new RpcException(RpcError.InvalidParams.WithData($"Unsupported parameter type: {targetType}")); - } + private static readonly Dictionary> s_conversionStrategies; - private static object ConvertNumeric(JToken token) where T : struct - { - if (TryConvertDoubleToNumericType(token, out var result)) + static ParameterConverter() { - return result; + s_conversionStrategies = new Dictionary> + { + { typeof(string), token => Result.Ok_Or(token.AsString, CreateInvalidParamError(token)) }, + { typeof(byte), ConvertNumeric }, + { typeof(sbyte), ConvertNumeric }, + { typeof(short), ConvertNumeric }, + { typeof(ushort), ConvertNumeric }, + { typeof(int), ConvertNumeric }, + { typeof(uint), ConvertNumeric }, + { typeof(long), ConvertNumeric }, + { typeof(ulong), ConvertNumeric }, + { typeof(double), token => Result.Ok_Or(token.AsNumber, CreateInvalidParamError(token)) }, + { typeof(bool), token => Result.Ok_Or(token.AsBoolean, CreateInvalidParamError(token)) }, + { typeof(UInt256), ConvertUInt256 }, + { typeof(ContractNameOrHashOrId), ConvertContractNameOrHashOrId }, + { typeof(BlockHashOrIndex), ConvertBlockHashOrIndex } + }; } - throw new RpcException(CreateInvalidParamError(token)); - } - - private static bool TryConvertDoubleToNumericType(JToken token, out T result) where T : struct - { - result = default; - try + internal static object ConvertParameter(JToken token, Type targetType) { - var value = token.AsNumber(); - var minValue = Convert.ToDouble(typeof(T).GetField("MinValue").GetValue(null)); - var maxValue = Convert.ToDouble(typeof(T).GetField("MaxValue").GetValue(null)); + if (s_conversionStrategies.TryGetValue(targetType, out var conversionStrategy)) + return conversionStrategy(token); + throw new RpcException(RpcError.InvalidParams.WithData($"Unsupported parameter type: {targetType}")); + } - if (value < minValue || value > maxValue) + private static object ConvertNumeric(JToken token) where T : struct + { + if (TryConvertDoubleToNumericType(token, out var result)) { - return false; + return result; } - if (!typeof(T).IsFloatingPoint() && !IsValidInteger(value)) + throw new RpcException(CreateInvalidParamError(token)); + } + + private static bool TryConvertDoubleToNumericType(JToken token, out T result) where T : struct + { + result = default; + try + { + var value = token.AsNumber(); + var minValue = Convert.ToDouble(typeof(T).GetField("MinValue").GetValue(null)); + var maxValue = Convert.ToDouble(typeof(T).GetField("MaxValue").GetValue(null)); + + if (value < minValue || value > maxValue) + { + return false; + } + + if (!typeof(T).IsFloatingPoint() && !IsValidInteger(value)) + { + return false; + } + + result = (T)Convert.ChangeType(value, typeof(T)); + return true; + } + catch { return false; } - - result = (T)Convert.ChangeType(value, typeof(T)); - return true; } - catch + + private static bool IsValidInteger(double value) { - return false; + // Integer values are safe if they are within the range of MIN_SAFE_INTEGER and MAX_SAFE_INTEGER + if (value < JNumber.MIN_SAFE_INTEGER || value > JNumber.MAX_SAFE_INTEGER) + return false; + return Math.Abs(value % 1) <= double.Epsilon; } - } - - private static bool IsValidInteger(double value) - { - // Integer values are safe if they are within the range of MIN_SAFE_INTEGER and MAX_SAFE_INTEGER - if (value < JNumber.MIN_SAFE_INTEGER || value > JNumber.MAX_SAFE_INTEGER) - return false; - return Math.Abs(value % 1) <= double.Epsilon; - } - internal static object ConvertUInt160(JToken token, byte addressVersion) - { - var value = token.AsString(); - if (UInt160.TryParse(value, out var scriptHash)) + internal static object ConvertUInt160(JToken token, byte addressVersion) { - return scriptHash; + var value = token.AsString(); + if (UInt160.TryParse(value, out var scriptHash)) + { + return scriptHash; + } + return Result.Ok_Or(() => value.ToScriptHash(addressVersion), + RpcError.InvalidParams.WithData($"Invalid UInt160 Format: {token}")); } - return Result.Ok_Or(() => value.ToScriptHash(addressVersion), - RpcError.InvalidParams.WithData($"Invalid UInt160 Format: {token}")); - } - private static object ConvertUInt256(JToken token) - { - if (UInt256.TryParse(token.AsString(), out var hash)) + private static object ConvertUInt256(JToken token) { - return hash; + if (UInt256.TryParse(token.AsString(), out var hash)) + { + return hash; + } + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid UInt256 Format: {token}")); } - throw new RpcException(RpcError.InvalidParams.WithData($"Invalid UInt256 Format: {token}")); - } - private static object ConvertContractNameOrHashOrId(JToken token) - { - if (ContractNameOrHashOrId.TryParse(token.AsString(), out var contractNameOrHashOrId)) + private static object ConvertContractNameOrHashOrId(JToken token) { - return contractNameOrHashOrId; + if (ContractNameOrHashOrId.TryParse(token.AsString(), out var contractNameOrHashOrId)) + { + return contractNameOrHashOrId; + } + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid contract hash or id Format: {token}")); } - throw new RpcException(RpcError.InvalidParams.WithData($"Invalid contract hash or id Format: {token}")); - } - private static object ConvertBlockHashOrIndex(JToken token) - { - if (BlockHashOrIndex.TryParse(token.AsString(), out var blockHashOrIndex)) + private static object ConvertBlockHashOrIndex(JToken token) { - return blockHashOrIndex; + if (BlockHashOrIndex.TryParse(token.AsString(), out var blockHashOrIndex)) + { + return blockHashOrIndex; + } + throw new RpcException(RpcError.InvalidParams.WithData($"Invalid block hash or index Format: {token}")); } - throw new RpcException(RpcError.InvalidParams.WithData($"Invalid block hash or index Format: {token}")); - } - private static RpcError CreateInvalidParamError(JToken token) - { - return RpcError.InvalidParams.WithData($"Invalid {typeof(T)} value: {token}"); + private static RpcError CreateInvalidParamError(JToken token) + { + return RpcError.InvalidParams.WithData($"Invalid {typeof(T)} value: {token}"); + } } -} -public static class TypeExtensions -{ - public static bool IsFloatingPoint(this Type type) + public static class TypeExtensions { - return type == typeof(float) || type == typeof(double) || type == typeof(decimal); + public static bool IsFloatingPoint(this Type type) + { + return type == typeof(float) || type == typeof(double) || type == typeof(decimal); + } } } diff --git a/src/Plugins/RpcServer/RpcMethodWithParamsAttribute.cs b/src/Plugins/RpcServer/RpcMethodWithParamsAttribute.cs index c53dd1fd85..cb14562717 100644 --- a/src/Plugins/RpcServer/RpcMethodWithParamsAttribute.cs +++ b/src/Plugins/RpcServer/RpcMethodWithParamsAttribute.cs @@ -11,10 +11,11 @@ using System; -namespace Neo.Plugins.RpcServer; - -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] -public class RpcMethodWithParamsAttribute : Attribute +namespace Neo.Plugins.RpcServer { - public string Name { get; set; } + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class RpcMethodWithParamsAttribute : Attribute + { + public string Name { get; set; } + } } diff --git a/src/Plugins/RpcServer/RpcServer.Node.cs b/src/Plugins/RpcServer/RpcServer.Node.cs index 23c731d01e..cfc66e932a 100644 --- a/src/Plugins/RpcServer/RpcServer.Node.cs +++ b/src/Plugins/RpcServer/RpcServer.Node.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Akka.Actor; -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.Ledger; using Neo.Network.P2P; diff --git a/src/Plugins/RpcServer/RpcServer.Wallet.cs b/src/Plugins/RpcServer/RpcServer.Wallet.cs index 25a7333aac..61dd03675b 100644 --- a/src/Plugins/RpcServer/RpcServer.Wallet.cs +++ b/src/Plugins/RpcServer/RpcServer.Wallet.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Akka.Actor; -using Neo.IO; +using Neo.Extensions; using Neo.Json; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/src/Plugins/RpcServer/RpcServer.csproj b/src/Plugins/RpcServer/RpcServer.csproj index c5b72e7a61..80618b13e3 100644 --- a/src/Plugins/RpcServer/RpcServer.csproj +++ b/src/Plugins/RpcServer/RpcServer.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Plugins.RpcServer ../../../bin/$(PackageId) diff --git a/src/Plugins/SQLiteWallet/Account.cs b/src/Plugins/SQLiteWallet/Account.cs index e7ae09af90..cdedd3b237 100644 --- a/src/Plugins/SQLiteWallet/Account.cs +++ b/src/Plugins/SQLiteWallet/Account.cs @@ -9,10 +9,11 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Wallets.SQLite; - -class Account +namespace Neo.Wallets.SQLite { - public byte[] PublicKeyHash { get; set; } - public string Nep2key { get; set; } + class Account + { + public byte[] PublicKeyHash { get; set; } + public string Nep2key { get; set; } + } } diff --git a/src/Plugins/SQLiteWallet/Address.cs b/src/Plugins/SQLiteWallet/Address.cs index 6f2b73e427..5147ea6f46 100644 --- a/src/Plugins/SQLiteWallet/Address.cs +++ b/src/Plugins/SQLiteWallet/Address.cs @@ -9,9 +9,10 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Wallets.SQLite; - -class Address +namespace Neo.Wallets.SQLite { - public byte[] ScriptHash { get; set; } + class Address + { + public byte[] ScriptHash { get; set; } + } } diff --git a/src/Plugins/SQLiteWallet/Contract.cs b/src/Plugins/SQLiteWallet/Contract.cs index 2da432a63b..c67c97fd52 100644 --- a/src/Plugins/SQLiteWallet/Contract.cs +++ b/src/Plugins/SQLiteWallet/Contract.cs @@ -9,13 +9,14 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Wallets.SQLite; - -class Contract +namespace Neo.Wallets.SQLite { - public byte[] RawData { get; set; } - public byte[] ScriptHash { get; set; } - public byte[] PublicKeyHash { get; set; } - public Account Account { get; set; } - public Address Address { get; set; } + class Contract + { + public byte[] RawData { get; set; } + public byte[] ScriptHash { get; set; } + public byte[] PublicKeyHash { get; set; } + public Account Account { get; set; } + public Address Address { get; set; } + } } diff --git a/src/Plugins/SQLiteWallet/Key.cs b/src/Plugins/SQLiteWallet/Key.cs index 81a8a6daf4..7c3bf4bc80 100644 --- a/src/Plugins/SQLiteWallet/Key.cs +++ b/src/Plugins/SQLiteWallet/Key.cs @@ -9,10 +9,11 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Wallets.SQLite; - -class Key +namespace Neo.Wallets.SQLite { - public string Name { get; set; } - public byte[] Value { get; set; } + class Key + { + public string Name { get; set; } + public byte[] Value { get; set; } + } } diff --git a/src/Plugins/SQLiteWallet/SQLiteWallet.cs b/src/Plugins/SQLiteWallet/SQLiteWallet.cs index a4004dcff0..d7d4fab9d6 100644 --- a/src/Plugins/SQLiteWallet/SQLiteWallet.cs +++ b/src/Plugins/SQLiteWallet/SQLiteWallet.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore; using Neo.Cryptography; -using Neo.IO; +using Neo.Extensions; using Neo.SmartContract; using Neo.Wallets.NEP6; using System.Buffers.Binary; @@ -20,397 +20,398 @@ using System.Text; using static System.IO.Path; -namespace Neo.Wallets.SQLite; - -/// -/// A wallet implementation that uses SQLite as the underlying storage. -/// -class SQLiteWallet : Wallet +namespace Neo.Wallets.SQLite { - private readonly object db_lock = new(); - private readonly byte[] iv; - private readonly byte[] salt; - private readonly byte[] masterKey; - private readonly ScryptParameters scrypt; - private readonly Dictionary accounts; + /// + /// A wallet implementation that uses SQLite as the underlying storage. + /// + class SQLiteWallet : Wallet + { + private readonly object db_lock = new(); + private readonly byte[] iv; + private readonly byte[] salt; + private readonly byte[] masterKey; + private readonly ScryptParameters scrypt; + private readonly Dictionary accounts; - public override string Name => GetFileNameWithoutExtension(Path); + public override string Name => GetFileNameWithoutExtension(Path); - public override Version Version - { - get + public override Version Version { - byte[] buffer = LoadStoredData("Version"); - if (buffer == null || buffer.Length < 16) return new Version(0, 0); - int major = BinaryPrimitives.ReadInt32LittleEndian(buffer); - int minor = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(4)); - int build = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(8)); - int revision = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(12)); - return new Version(major, minor, build, revision); + get + { + byte[] buffer = LoadStoredData("Version"); + if (buffer == null || buffer.Length < 16) return new Version(0, 0); + int major = BinaryPrimitives.ReadInt32LittleEndian(buffer); + int minor = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(4)); + int build = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(8)); + int revision = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(12)); + return new Version(major, minor, build, revision); + } } - } - - private SQLiteWallet(string path, byte[] passwordKey, ProtocolSettings settings) : base(path, settings) - { - salt = LoadStoredData("Salt"); - byte[] passwordHash = LoadStoredData("PasswordHash"); - if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) - throw new CryptographicException(); - iv = LoadStoredData("IV"); - masterKey = Decrypt(LoadStoredData("MasterKey"), passwordKey, iv); - scrypt = new ScryptParameters - ( - BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptN")), - BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptR")), - BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptP")) - ); - accounts = LoadAccounts(); - } - private SQLiteWallet(string path, byte[] passwordKey, ProtocolSettings settings, ScryptParameters scrypt) : base(path, settings) - { - iv = new byte[16]; - salt = new byte[20]; - masterKey = new byte[32]; - this.scrypt = scrypt; - accounts = new Dictionary(); - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + private SQLiteWallet(string path, byte[] passwordKey, ProtocolSettings settings) : base(path, settings) { - rng.GetBytes(iv); - rng.GetBytes(salt); - rng.GetBytes(masterKey); + salt = LoadStoredData("Salt"); + byte[] passwordHash = LoadStoredData("PasswordHash"); + if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) + throw new CryptographicException(); + iv = LoadStoredData("IV"); + masterKey = Decrypt(LoadStoredData("MasterKey"), passwordKey, iv); + scrypt = new ScryptParameters + ( + BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptN")), + BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptR")), + BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptP")) + ); + accounts = LoadAccounts(); } - Version version = Assembly.GetExecutingAssembly().GetName().Version; - byte[] versionBuffer = new byte[sizeof(int) * 4]; - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer, version.Major); - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(4), version.Minor); - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(8), version.Build); - BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(12), version.Revision); - BuildDatabase(); - SaveStoredData("IV", iv); - SaveStoredData("Salt", salt); - SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); - SaveStoredData("Version", versionBuffer); - SaveStoredData("ScryptN", this.scrypt.N); - SaveStoredData("ScryptR", this.scrypt.R); - SaveStoredData("ScryptP", this.scrypt.P); - } - private void AddAccount(SQLiteWalletAccount account) - { - lock (accounts) + private SQLiteWallet(string path, byte[] passwordKey, ProtocolSettings settings, ScryptParameters scrypt) : base(path, settings) { - if (accounts.TryGetValue(account.ScriptHash, out SQLiteWalletAccount account_old)) + iv = new byte[16]; + salt = new byte[20]; + masterKey = new byte[32]; + this.scrypt = scrypt; + accounts = new Dictionary(); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { - if (account.Contract == null) - { - account.Contract = account_old.Contract; - } + rng.GetBytes(iv); + rng.GetBytes(salt); + rng.GetBytes(masterKey); } - accounts[account.ScriptHash] = account; + Version version = Assembly.GetExecutingAssembly().GetName().Version; + byte[] versionBuffer = new byte[sizeof(int) * 4]; + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer, version.Major); + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(4), version.Minor); + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(8), version.Build); + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(12), version.Revision); + BuildDatabase(); + SaveStoredData("IV", iv); + SaveStoredData("Salt", salt); + SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); + SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); + SaveStoredData("Version", versionBuffer); + SaveStoredData("ScryptN", this.scrypt.N); + SaveStoredData("ScryptR", this.scrypt.R); + SaveStoredData("ScryptP", this.scrypt.P); } - lock (db_lock) + + private void AddAccount(SQLiteWalletAccount account) { - using WalletDataContext ctx = new(Path); - if (account.HasKey) + lock (accounts) { - Account db_account = ctx.Accounts.FirstOrDefault(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); - if (db_account == null) + if (accounts.TryGetValue(account.ScriptHash, out SQLiteWalletAccount account_old)) { - db_account = ctx.Accounts.Add(new Account + if (account.Contract == null) { - Nep2key = account.Key.Export(masterKey, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P), - PublicKeyHash = account.Key.PublicKeyHash.ToArray() - }).Entity; - } - else - { - db_account.Nep2key = account.Key.Export(masterKey, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P); + account.Contract = account_old.Contract; + } } + accounts[account.ScriptHash] = account; } - if (account.Contract != null) + lock (db_lock) { - Contract db_contract = ctx.Contracts.FirstOrDefault(p => p.ScriptHash == account.Contract.ScriptHash.ToArray()); - if (db_contract != null) + using WalletDataContext ctx = new(Path); + if (account.HasKey) { - db_contract.PublicKeyHash = account.Key.PublicKeyHash.ToArray(); + Account db_account = ctx.Accounts.FirstOrDefault(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); + if (db_account == null) + { + db_account = ctx.Accounts.Add(new Account + { + Nep2key = account.Key.Export(masterKey, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P), + PublicKeyHash = account.Key.PublicKeyHash.ToArray() + }).Entity; + } + else + { + db_account.Nep2key = account.Key.Export(masterKey, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P); + } } - else + if (account.Contract != null) { - ctx.Contracts.Add(new Contract + Contract db_contract = ctx.Contracts.FirstOrDefault(p => p.ScriptHash == account.Contract.ScriptHash.ToArray()); + if (db_contract != null) { - RawData = ((VerificationContract)account.Contract).ToArray(), - ScriptHash = account.Contract.ScriptHash.ToArray(), - PublicKeyHash = account.Key.PublicKeyHash.ToArray() - }); + db_contract.PublicKeyHash = account.Key.PublicKeyHash.ToArray(); + } + else + { + ctx.Contracts.Add(new Contract + { + RawData = ((VerificationContract)account.Contract).ToArray(), + ScriptHash = account.Contract.ScriptHash.ToArray(), + PublicKeyHash = account.Key.PublicKeyHash.ToArray() + }); + } } - } - //add address - { - Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.ScriptHash.ToArray()); - if (db_address == null) + //add address { - ctx.Addresses.Add(new Address + Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.ScriptHash.ToArray()); + if (db_address == null) { - ScriptHash = account.ScriptHash.ToArray() - }); + ctx.Addresses.Add(new Address + { + ScriptHash = account.ScriptHash.ToArray() + }); + } } + ctx.SaveChanges(); } - ctx.SaveChanges(); } - } - - private void BuildDatabase() - { - using WalletDataContext ctx = new(Path); - ctx.Database.EnsureDeleted(); - ctx.Database.EnsureCreated(); - } - public override bool ChangePassword(string oldPassword, string newPassword) - { - if (!VerifyPassword(oldPassword)) return false; - byte[] passwordKey = ToAesKey(newPassword); - try + private void BuildDatabase() { - SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); - return true; + using WalletDataContext ctx = new(Path); + ctx.Database.EnsureDeleted(); + ctx.Database.EnsureCreated(); } - finally + + public override bool ChangePassword(string oldPassword, string newPassword) { - Array.Clear(passwordKey, 0, passwordKey.Length); + if (!VerifyPassword(oldPassword)) return false; + byte[] passwordKey = ToAesKey(newPassword); + try + { + SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); + SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); + return true; + } + finally + { + Array.Clear(passwordKey, 0, passwordKey.Length); + } } - } - public override bool Contains(UInt160 scriptHash) - { - lock (accounts) + public override bool Contains(UInt160 scriptHash) { - return accounts.ContainsKey(scriptHash); + lock (accounts) + { + return accounts.ContainsKey(scriptHash); + } } - } - /// - /// Creates a new wallet at the specified path. - /// - /// The path of the wallet. - /// The password of the wallet. - /// The to be used by the wallet. - /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. - /// The created wallet. - public static SQLiteWallet Create(string path, string password, ProtocolSettings settings, ScryptParameters scrypt = null) - { - return new SQLiteWallet(path, ToAesKey(password), settings, scrypt ?? ScryptParameters.Default); - } - - public override WalletAccount CreateAccount(byte[] privateKey) - { - KeyPair key = new(privateKey); - VerificationContract contract = new() - { - Script = SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), - ParameterList = new[] { ContractParameterType.Signature } - }; - SQLiteWalletAccount account = new(contract.ScriptHash, ProtocolSettings) + /// + /// Creates a new wallet at the specified path. + /// + /// The path of the wallet. + /// The password of the wallet. + /// The to be used by the wallet. + /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. + /// The created wallet. + public static SQLiteWallet Create(string path, string password, ProtocolSettings settings, ScryptParameters scrypt = null) { - Key = key, - Contract = contract - }; - AddAccount(account); - return account; - } + return new SQLiteWallet(path, ToAesKey(password), settings, scrypt ?? ScryptParameters.Default); + } - public override WalletAccount CreateAccount(SmartContract.Contract contract, KeyPair key = null) - { - if (contract is not VerificationContract verification_contract) + public override WalletAccount CreateAccount(byte[] privateKey) { - verification_contract = new VerificationContract + KeyPair key = new(privateKey); + VerificationContract contract = new() { - Script = contract.Script, - ParameterList = contract.ParameterList + Script = SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } }; + SQLiteWalletAccount account = new(contract.ScriptHash, ProtocolSettings) + { + Key = key, + Contract = contract + }; + AddAccount(account); + return account; } - SQLiteWalletAccount account = new(verification_contract.ScriptHash, ProtocolSettings) - { - Key = key, - Contract = verification_contract - }; - AddAccount(account); - return account; - } - public override WalletAccount CreateAccount(UInt160 scriptHash) - { - SQLiteWalletAccount account = new(scriptHash, ProtocolSettings); - AddAccount(account); - return account; - } + public override WalletAccount CreateAccount(SmartContract.Contract contract, KeyPair key = null) + { + if (contract is not VerificationContract verification_contract) + { + verification_contract = new VerificationContract + { + Script = contract.Script, + ParameterList = contract.ParameterList + }; + } + SQLiteWalletAccount account = new(verification_contract.ScriptHash, ProtocolSettings) + { + Key = key, + Contract = verification_contract + }; + AddAccount(account); + return account; + } - public override void Delete() - { - using WalletDataContext ctx = new(Path); - ctx.Database.EnsureDeleted(); - } + public override WalletAccount CreateAccount(UInt160 scriptHash) + { + SQLiteWalletAccount account = new(scriptHash, ProtocolSettings); + AddAccount(account); + return account; + } - public override bool DeleteAccount(UInt160 scriptHash) - { - SQLiteWalletAccount account; - lock (accounts) + public override void Delete() { - if (accounts.TryGetValue(scriptHash, out account)) - accounts.Remove(scriptHash); + using WalletDataContext ctx = new(Path); + ctx.Database.EnsureDeleted(); } - if (account != null) + + public override bool DeleteAccount(UInt160 scriptHash) { - lock (db_lock) + SQLiteWalletAccount account; + lock (accounts) { - using WalletDataContext ctx = new(Path); - if (account.HasKey) - { - Account db_account = ctx.Accounts.First(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); - ctx.Accounts.Remove(db_account); - } - if (account.Contract != null) - { - Contract db_contract = ctx.Contracts.First(p => p.ScriptHash == scriptHash.ToArray()); - ctx.Contracts.Remove(db_contract); - } - //delete address + if (accounts.TryGetValue(scriptHash, out account)) + accounts.Remove(scriptHash); + } + if (account != null) + { + lock (db_lock) { - Address db_address = ctx.Addresses.First(p => p.ScriptHash == scriptHash.ToArray()); - ctx.Addresses.Remove(db_address); + using WalletDataContext ctx = new(Path); + if (account.HasKey) + { + Account db_account = ctx.Accounts.First(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); + ctx.Accounts.Remove(db_account); + } + if (account.Contract != null) + { + Contract db_contract = ctx.Contracts.First(p => p.ScriptHash == scriptHash.ToArray()); + ctx.Contracts.Remove(db_contract); + } + //delete address + { + Address db_address = ctx.Addresses.First(p => p.ScriptHash == scriptHash.ToArray()); + ctx.Addresses.Remove(db_address); + } + ctx.SaveChanges(); } - ctx.SaveChanges(); + return true; } - return true; + return false; } - return false; - } - public override WalletAccount GetAccount(UInt160 scriptHash) - { - lock (accounts) + public override WalletAccount GetAccount(UInt160 scriptHash) { - accounts.TryGetValue(scriptHash, out SQLiteWalletAccount account); - return account; + lock (accounts) + { + accounts.TryGetValue(scriptHash, out SQLiteWalletAccount account); + return account; + } } - } - public override IEnumerable GetAccounts() - { - lock (accounts) + public override IEnumerable GetAccounts() { - foreach (SQLiteWalletAccount account in accounts.Values) - yield return account; + lock (accounts) + { + foreach (SQLiteWalletAccount account in accounts.Values) + yield return account; + } } - } - private Dictionary LoadAccounts() - { - using WalletDataContext ctx = new(Path); - Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new SQLiteWalletAccount(new UInt160(p), ProtocolSettings)).ToDictionary(p => p.ScriptHash); - foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) + private Dictionary LoadAccounts() { - VerificationContract contract = db_contract.RawData.AsSerializable(); - SQLiteWalletAccount account = accounts[contract.ScriptHash]; - account.Contract = contract; - account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, masterKey, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P)); + using WalletDataContext ctx = new(Path); + Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new SQLiteWalletAccount(new UInt160(p), ProtocolSettings)).ToDictionary(p => p.ScriptHash); + foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) + { + VerificationContract contract = db_contract.RawData.AsSerializable(); + SQLiteWalletAccount account = accounts[contract.ScriptHash]; + account.Contract = contract; + account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, masterKey, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P)); + } + return accounts; } - return accounts; - } - - private byte[] LoadStoredData(string name) - { - using WalletDataContext ctx = new(Path); - return ctx.Keys.FirstOrDefault(p => p.Name == name)?.Value; - } - /// - /// Opens a wallet at the specified path. - /// - /// The path of the wallet. - /// The password of the wallet. - /// The to be used by the wallet. - /// The opened wallet. - public static new SQLiteWallet Open(string path, string password, ProtocolSettings settings) - { - return new SQLiteWallet(path, ToAesKey(password), settings); - } + private byte[] LoadStoredData(string name) + { + using WalletDataContext ctx = new(Path); + return ctx.Keys.FirstOrDefault(p => p.Name == name)?.Value; + } - public override void Save() - { - // Do nothing - } + /// + /// Opens a wallet at the specified path. + /// + /// The path of the wallet. + /// The password of the wallet. + /// The to be used by the wallet. + /// The opened wallet. + public static new SQLiteWallet Open(string path, string password, ProtocolSettings settings) + { + return new SQLiteWallet(path, ToAesKey(password), settings); + } - private void SaveStoredData(string name, int value) - { - byte[] data = new byte[sizeof(int)]; - BinaryPrimitives.WriteInt32LittleEndian(data, value); - SaveStoredData(name, data); - } + public override void Save() + { + // Do nothing + } - private void SaveStoredData(string name, byte[] value) - { - lock (db_lock) + private void SaveStoredData(string name, int value) { - using WalletDataContext ctx = new(Path); - SaveStoredData(ctx, name, value); - ctx.SaveChanges(); + byte[] data = new byte[sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(data, value); + SaveStoredData(name, data); } - } - private static void SaveStoredData(WalletDataContext ctx, string name, byte[] value) - { - Key key = ctx.Keys.FirstOrDefault(p => p.Name == name); - if (key == null) + private void SaveStoredData(string name, byte[] value) { - ctx.Keys.Add(new Key + lock (db_lock) { - Name = name, - Value = value - }); + using WalletDataContext ctx = new(Path); + SaveStoredData(ctx, name, value); + ctx.SaveChanges(); + } } - else + + private static void SaveStoredData(WalletDataContext ctx, string name, byte[] value) { - key.Value = value; + Key key = ctx.Keys.FirstOrDefault(p => p.Name == name); + if (key == null) + { + ctx.Keys.Add(new Key + { + Name = name, + Value = value + }); + } + else + { + key.Value = value; + } } - } - public override bool VerifyPassword(string password) - { - return ToAesKey(password).Concat(salt).ToArray().Sha256().SequenceEqual(LoadStoredData("PasswordHash")); - } + public override bool VerifyPassword(string password) + { + return ToAesKey(password).Concat(salt).ToArray().Sha256().SequenceEqual(LoadStoredData("PasswordHash")); + } - private static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) - { - if (data == null || key == null || iv == null) throw new ArgumentNullException(); - if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); - using Aes aes = Aes.Create(); - aes.Padding = PaddingMode.None; - using ICryptoTransform encryptor = aes.CreateEncryptor(key, iv); - return encryptor.TransformFinalBlock(data, 0, data.Length); - } + private static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) + { + if (data == null || key == null || iv == null) throw new ArgumentNullException(); + if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); + using Aes aes = Aes.Create(); + aes.Padding = PaddingMode.None; + using ICryptoTransform encryptor = aes.CreateEncryptor(key, iv); + return encryptor.TransformFinalBlock(data, 0, data.Length); + } - private static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) - { - if (data == null || key == null || iv == null) throw new ArgumentNullException(); - if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); - using Aes aes = Aes.Create(); - aes.Padding = PaddingMode.None; - using ICryptoTransform decryptor = aes.CreateDecryptor(key, iv); - return decryptor.TransformFinalBlock(data, 0, data.Length); - } + private static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) + { + if (data == null || key == null || iv == null) throw new ArgumentNullException(); + if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); + using Aes aes = Aes.Create(); + aes.Padding = PaddingMode.None; + using ICryptoTransform decryptor = aes.CreateDecryptor(key, iv); + return decryptor.TransformFinalBlock(data, 0, data.Length); + } - private static byte[] ToAesKey(string password) - { - using SHA256 sha256 = SHA256.Create(); - byte[] passwordBytes = Encoding.UTF8.GetBytes(password); - byte[] passwordHash = sha256.ComputeHash(passwordBytes); - byte[] passwordHash2 = sha256.ComputeHash(passwordHash); - Array.Clear(passwordBytes, 0, passwordBytes.Length); - Array.Clear(passwordHash, 0, passwordHash.Length); - return passwordHash2; + private static byte[] ToAesKey(string password) + { + using SHA256 sha256 = SHA256.Create(); + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + byte[] passwordHash = sha256.ComputeHash(passwordBytes); + byte[] passwordHash2 = sha256.ComputeHash(passwordHash); + Array.Clear(passwordBytes, 0, passwordBytes.Length); + Array.Clear(passwordHash, 0, passwordHash.Length); + return passwordHash2; + } } } diff --git a/src/Plugins/SQLiteWallet/SQLiteWallet.csproj b/src/Plugins/SQLiteWallet/SQLiteWallet.csproj index b8a1646e65..f623a8c8cf 100644 --- a/src/Plugins/SQLiteWallet/SQLiteWallet.csproj +++ b/src/Plugins/SQLiteWallet/SQLiteWallet.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Wallets.SQLite Neo.Wallets.SQLite enable @@ -9,7 +9,7 @@ - + diff --git a/src/Plugins/SQLiteWallet/SQLiteWalletAccount.cs b/src/Plugins/SQLiteWallet/SQLiteWalletAccount.cs index 88526f7e52..01faee0e18 100644 --- a/src/Plugins/SQLiteWallet/SQLiteWalletAccount.cs +++ b/src/Plugins/SQLiteWallet/SQLiteWalletAccount.cs @@ -9,21 +9,22 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Wallets.SQLite; - -sealed class SQLiteWalletAccount : WalletAccount +namespace Neo.Wallets.SQLite { - public KeyPair Key; + sealed class SQLiteWalletAccount : WalletAccount + { + public KeyPair Key; - public override bool HasKey => Key != null; + public override bool HasKey => Key != null; - public SQLiteWalletAccount(UInt160 scriptHash, ProtocolSettings settings) - : base(scriptHash, settings) - { - } + public SQLiteWalletAccount(UInt160 scriptHash, ProtocolSettings settings) + : base(scriptHash, settings) + { + } - public override KeyPair GetKey() - { - return Key; + public override KeyPair GetKey() + { + return Key; + } } } diff --git a/src/Plugins/SQLiteWallet/SQLiteWalletFactory.cs b/src/Plugins/SQLiteWallet/SQLiteWalletFactory.cs index d952fd0fc3..069bc36dcc 100644 --- a/src/Plugins/SQLiteWallet/SQLiteWalletFactory.cs +++ b/src/Plugins/SQLiteWallet/SQLiteWalletFactory.cs @@ -12,30 +12,31 @@ using Neo.Plugins; using static System.IO.Path; -namespace Neo.Wallets.SQLite; - -public class SQLiteWalletFactory : Plugin, IWalletFactory +namespace Neo.Wallets.SQLite { - public override string Name => "SQLiteWallet"; - public override string Description => "A SQLite-based wallet provider that supports wallet files with .db3 suffix."; - - public SQLiteWalletFactory() + public class SQLiteWalletFactory : Plugin, IWalletFactory { - Wallet.RegisterFactory(this); - } + public override string Name => "SQLiteWallet"; + public override string Description => "A SQLite-based wallet provider that supports wallet files with .db3 suffix."; - public bool Handle(string path) - { - return GetExtension(path).ToLowerInvariant() == ".db3"; - } + public SQLiteWalletFactory() + { + Wallet.RegisterFactory(this); + } - public Wallet CreateWallet(string name, string path, string password, ProtocolSettings settings) - { - return SQLiteWallet.Create(path, password, settings); - } + public bool Handle(string path) + { + return GetExtension(path).ToLowerInvariant() == ".db3"; + } - public Wallet OpenWallet(string path, string password, ProtocolSettings settings) - { - return SQLiteWallet.Open(path, password, settings); + public Wallet CreateWallet(string name, string path, string password, ProtocolSettings settings) + { + return SQLiteWallet.Create(path, password, settings); + } + + public Wallet OpenWallet(string path, string password, ProtocolSettings settings) + { + return SQLiteWallet.Open(path, password, settings); + } } } diff --git a/src/Plugins/SQLiteWallet/VerificationContract.cs b/src/Plugins/SQLiteWallet/VerificationContract.cs index 2f62a7174b..5508046ca3 100644 --- a/src/Plugins/SQLiteWallet/VerificationContract.cs +++ b/src/Plugins/SQLiteWallet/VerificationContract.cs @@ -13,45 +13,46 @@ using Neo.IO; using Neo.SmartContract; -namespace Neo.Wallets.SQLite; - -class VerificationContract : SmartContract.Contract, IEquatable, ISerializable +namespace Neo.Wallets.SQLite { - public int Size => ParameterList.GetVarSize() + Script.GetVarSize(); - - public void Deserialize(ref MemoryReader reader) + class VerificationContract : SmartContract.Contract, IEquatable, ISerializable { - ReadOnlySpan span = reader.ReadVarMemory().Span; - ParameterList = new ContractParameterType[span.Length]; - for (int i = 0; i < span.Length; i++) + public int Size => ParameterList.GetVarSize() + Script.GetVarSize(); + + public void Deserialize(ref MemoryReader reader) { - ParameterList[i] = (ContractParameterType)span[i]; - if (!Enum.IsDefined(typeof(ContractParameterType), ParameterList[i])) - throw new FormatException(); + ReadOnlySpan span = reader.ReadVarMemory().Span; + ParameterList = new ContractParameterType[span.Length]; + for (int i = 0; i < span.Length; i++) + { + ParameterList[i] = (ContractParameterType)span[i]; + if (!Enum.IsDefined(typeof(ContractParameterType), ParameterList[i])) + throw new FormatException(); + } + Script = reader.ReadVarMemory().ToArray(); } - Script = reader.ReadVarMemory().ToArray(); - } - public bool Equals(VerificationContract other) - { - if (ReferenceEquals(this, other)) return true; - if (other is null) return false; - return ScriptHash.Equals(other.ScriptHash); - } + public bool Equals(VerificationContract other) + { + if (ReferenceEquals(this, other)) return true; + if (other is null) return false; + return ScriptHash.Equals(other.ScriptHash); + } - public override bool Equals(object obj) - { - return Equals(obj as VerificationContract); - } + public override bool Equals(object obj) + { + return Equals(obj as VerificationContract); + } - public override int GetHashCode() - { - return ScriptHash.GetHashCode(); - } + public override int GetHashCode() + { + return ScriptHash.GetHashCode(); + } - public void Serialize(BinaryWriter writer) - { - writer.WriteVarBytes(ParameterList.Select(p => (byte)p).ToArray()); - writer.WriteVarBytes(Script); + public void Serialize(BinaryWriter writer) + { + writer.WriteVarBytes(ParameterList.Select(p => (byte)p).ToArray()); + writer.WriteVarBytes(Script); + } } } diff --git a/src/Plugins/SQLiteWallet/WalletDataContext.cs b/src/Plugins/SQLiteWallet/WalletDataContext.cs index 79ca538cd1..eef22c846d 100644 --- a/src/Plugins/SQLiteWallet/WalletDataContext.cs +++ b/src/Plugins/SQLiteWallet/WalletDataContext.cs @@ -12,53 +12,54 @@ using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; -namespace Neo.Wallets.SQLite; - -class WalletDataContext : DbContext +namespace Neo.Wallets.SQLite { - public DbSet Accounts { get; set; } - public DbSet
Addresses { get; set; } - public DbSet Contracts { get; set; } - public DbSet Keys { get; set; } + class WalletDataContext : DbContext + { + public DbSet Accounts { get; set; } + public DbSet
Addresses { get; set; } + public DbSet Contracts { get; set; } + public DbSet Keys { get; set; } - private readonly string filename; + private readonly string filename; - public WalletDataContext(string filename) - { - this.filename = filename; - } + public WalletDataContext(string filename) + { + this.filename = filename; + } - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - SqliteConnectionStringBuilder sb = new() + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - DataSource = filename - }; - optionsBuilder.UseSqlite(sb.ToString()); - } + base.OnConfiguring(optionsBuilder); + SqliteConnectionStringBuilder sb = new() + { + DataSource = filename + }; + optionsBuilder.UseSqlite(sb.ToString()); + } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - modelBuilder.Entity().ToTable(nameof(Account)); - modelBuilder.Entity().HasKey(p => p.PublicKeyHash); - modelBuilder.Entity().Property(p => p.Nep2key).HasColumnType("VarChar").HasMaxLength(byte.MaxValue).IsRequired(); - modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity
().ToTable(nameof(Address)); - modelBuilder.Entity
().HasKey(p => p.ScriptHash); - modelBuilder.Entity
().Property(p => p.ScriptHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().ToTable(nameof(Contract)); - modelBuilder.Entity().HasKey(p => p.ScriptHash); - modelBuilder.Entity().HasIndex(p => p.PublicKeyHash); - modelBuilder.Entity().HasOne(p => p.Account).WithMany().HasForeignKey(p => p.PublicKeyHash).OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity().HasOne(p => p.Address).WithMany().HasForeignKey(p => p.ScriptHash).OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity().Property(p => p.RawData).HasColumnType("VarBinary").IsRequired(); - modelBuilder.Entity().Property(p => p.ScriptHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().ToTable(nameof(Key)); - modelBuilder.Entity().HasKey(p => p.Name); - modelBuilder.Entity().Property(p => p.Name).HasColumnType("VarChar").HasMaxLength(20).IsRequired(); - modelBuilder.Entity().Property(p => p.Value).HasColumnType("VarBinary").IsRequired(); + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity().ToTable(nameof(Account)); + modelBuilder.Entity().HasKey(p => p.PublicKeyHash); + modelBuilder.Entity().Property(p => p.Nep2key).HasColumnType("VarChar").HasMaxLength(byte.MaxValue).IsRequired(); + modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); + modelBuilder.Entity
().ToTable(nameof(Address)); + modelBuilder.Entity
().HasKey(p => p.ScriptHash); + modelBuilder.Entity
().Property(p => p.ScriptHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); + modelBuilder.Entity().ToTable(nameof(Contract)); + modelBuilder.Entity().HasKey(p => p.ScriptHash); + modelBuilder.Entity().HasIndex(p => p.PublicKeyHash); + modelBuilder.Entity().HasOne(p => p.Account).WithMany().HasForeignKey(p => p.PublicKeyHash).OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(p => p.Address).WithMany().HasForeignKey(p => p.ScriptHash).OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().Property(p => p.RawData).HasColumnType("VarBinary").IsRequired(); + modelBuilder.Entity().Property(p => p.ScriptHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); + modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); + modelBuilder.Entity().ToTable(nameof(Key)); + modelBuilder.Entity().HasKey(p => p.Name); + modelBuilder.Entity().Property(p => p.Name).HasColumnType("VarChar").HasMaxLength(20).IsRequired(); + modelBuilder.Entity().Property(p => p.Value).HasColumnType("VarBinary").IsRequired(); + } } } diff --git a/src/Plugins/StateService/Network/StateRoot.cs b/src/Plugins/StateService/Network/StateRoot.cs index 5b4e8610f2..7b3b7e9706 100644 --- a/src/Plugins/StateService/Network/StateRoot.cs +++ b/src/Plugins/StateService/Network/StateRoot.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P; diff --git a/src/Plugins/StateService/StatePlugin.cs b/src/Plugins/StateService/StatePlugin.cs index 03dcc55aac..a514f22ce7 100644 --- a/src/Plugins/StateService/StatePlugin.cs +++ b/src/Plugins/StateService/StatePlugin.cs @@ -12,8 +12,8 @@ using Akka.Actor; using Neo.ConsoleService; using Neo.Cryptography.MPTTrie; +using Neo.Extensions; using Neo.IEventHandlers; -using Neo.IO; using Neo.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/src/Plugins/StateService/StateService.csproj b/src/Plugins/StateService/StateService.csproj index 58c18ac498..d2475c4cb2 100644 --- a/src/Plugins/StateService/StateService.csproj +++ b/src/Plugins/StateService/StateService.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Plugins.StateService true ../../../bin/$(PackageId) diff --git a/src/Plugins/StateService/Storage/StateSnapshot.cs b/src/Plugins/StateService/Storage/StateSnapshot.cs index 70ec006227..6dc1b323fd 100644 --- a/src/Plugins/StateService/Storage/StateSnapshot.cs +++ b/src/Plugins/StateService/Storage/StateSnapshot.cs @@ -10,7 +10,7 @@ // modifications are permitted. using Neo.Cryptography.MPTTrie; -using Neo.IO; +using Neo.Extensions; using Neo.Persistence; using Neo.Plugins.StateService.Network; using System; diff --git a/src/Plugins/StorageDumper/StorageDumper.cs b/src/Plugins/StorageDumper/StorageDumper.cs index 6f5498b3a2..184b6f61ac 100644 --- a/src/Plugins/StorageDumper/StorageDumper.cs +++ b/src/Plugins/StorageDumper/StorageDumper.cs @@ -10,8 +10,8 @@ // modifications are permitted. using Neo.ConsoleService; +using Neo.Extensions; using Neo.IEventHandlers; -using Neo.IO; using Neo.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/src/Plugins/StorageDumper/StorageDumper.csproj b/src/Plugins/StorageDumper/StorageDumper.csproj index ebadda6136..abf6469ade 100644 --- a/src/Plugins/StorageDumper/StorageDumper.csproj +++ b/src/Plugins/StorageDumper/StorageDumper.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Plugins.StorageDumper enable enable diff --git a/src/Plugins/TokensTracker/Extensions.cs b/src/Plugins/TokensTracker/Extensions.cs index 83b4371435..2eaea3bfd6 100644 --- a/src/Plugins/TokensTracker/Extensions.cs +++ b/src/Plugins/TokensTracker/Extensions.cs @@ -28,7 +28,7 @@ public static bool NotNull(this StackItem item) public static string ToBase64(this ReadOnlySpan item) { - return item == null ? String.Empty : Convert.ToBase64String(item); + return item.IsEmpty ? String.Empty : Convert.ToBase64String(item); } public static int GetVarSize(this ByteString item) diff --git a/src/Plugins/TokensTracker/TokensTracker.csproj b/src/Plugins/TokensTracker/TokensTracker.csproj index 5f17120159..802f71f1ae 100644 --- a/src/Plugins/TokensTracker/TokensTracker.csproj +++ b/src/Plugins/TokensTracker/TokensTracker.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Plugins.TokensTracker ../../../bin/$(PackageId) diff --git a/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11BalanceKey.cs b/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11BalanceKey.cs index 1f375f33d9..2965985653 100644 --- a/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11BalanceKey.cs +++ b/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11BalanceKey.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.VM.Types; using System; diff --git a/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11TransferKey.cs b/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11TransferKey.cs index 5c999e8e00..9248267785 100644 --- a/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11TransferKey.cs +++ b/src/Plugins/TokensTracker/Trackers/NEP-11/Nep11TransferKey.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.VM.Types; using System; diff --git a/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17BalanceKey.cs b/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17BalanceKey.cs index bbceabec2b..817d789e5c 100644 --- a/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17BalanceKey.cs +++ b/src/Plugins/TokensTracker/Trackers/NEP-17/Nep17BalanceKey.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System; using System.IO; diff --git a/src/Plugins/TokensTracker/Trackers/TokenBalance.cs b/src/Plugins/TokensTracker/Trackers/TokenBalance.cs index f54a7c9856..a171fa40d7 100644 --- a/src/Plugins/TokensTracker/Trackers/TokenBalance.cs +++ b/src/Plugins/TokensTracker/Trackers/TokenBalance.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System.IO; using System.Numerics; diff --git a/src/Plugins/TokensTracker/Trackers/TokenTransfer.cs b/src/Plugins/TokensTracker/Trackers/TokenTransfer.cs index 0a221850fc..9bb389b516 100644 --- a/src/Plugins/TokensTracker/Trackers/TokenTransfer.cs +++ b/src/Plugins/TokensTracker/Trackers/TokenTransfer.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System.IO; using System.Numerics; diff --git a/src/Plugins/TokensTracker/Trackers/TokenTransferKey.cs b/src/Plugins/TokensTracker/Trackers/TokenTransferKey.cs index 252eb20af0..3c59131749 100644 --- a/src/Plugins/TokensTracker/Trackers/TokenTransferKey.cs +++ b/src/Plugins/TokensTracker/Trackers/TokenTransferKey.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using System; using System.Buffers.Binary; diff --git a/src/Plugins/TokensTracker/Trackers/TrackerBase.cs b/src/Plugins/TokensTracker/Trackers/TrackerBase.cs index 471362b019..40009e6d85 100644 --- a/src/Plugins/TokensTracker/Trackers/TrackerBase.cs +++ b/src/Plugins/TokensTracker/Trackers/TrackerBase.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Ledger; diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index a2fc251365..e4089c9a68 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj b/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj index 469a49e548..3e25625ce3 100644 --- a/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj +++ b/tests/Neo.ConsoleService.Tests/Neo.ConsoleService.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 neo_cli.Tests @@ -10,9 +10,9 @@ - - - + + + diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj b/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj index 827c547041..57167ed73b 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj +++ b/tests/Neo.Cryptography.BLS12_381.Tests/Neo.Cryptography.BLS12_381.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable @@ -11,9 +11,9 @@ - - - + + + diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs index bc9cd480f2..15a8a6ff49 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs @@ -13,341 +13,342 @@ using System.Runtime.InteropServices; using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_Fp +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestSize() + [TestClass] + public class UT_Fp { - Assert.AreEqual(Fp.Size, Marshal.SizeOf()); - } - - [TestMethod] - public void TestEquality() - { - static bool IsEqual(in Fp a, in Fp b) + [TestMethod] + public void TestSize() { - bool eq = StructuralComparisons.StructuralEqualityComparer.Equals(a, b); - bool ct_eq = a == b; - Assert.AreEqual(eq, ct_eq); - return eq; + Assert.AreEqual(Fp.Size, Marshal.SizeOf()); } - Assert.IsTrue(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - - Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 7, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 7, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 7, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 7, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 7, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 7 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); - } - - [TestMethod] - public void TestConditionalSelection() - { - Fp a = Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }); - Fp b = Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 }); - - Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); - Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); - } - - [TestMethod] - public void TestSquaring() - { - Fp a = Fp.FromRawUnchecked(new ulong[] - { - 0xd215_d276_8e83_191b, - 0x5085_d80f_8fb2_8261, - 0xce9a_032d_df39_3a56, - 0x3e9c_4fff_2ca0_c4bb, - 0x6436_b6f7_f4d9_5dfb, - 0x1060_6628_ad4a_4d90 - }); - Fp b = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestEquality() { - 0x33d9_c42a_3cb3_e235, - 0xdad1_1a09_4c4c_d455, - 0xa2f1_44bd_729a_aeba, - 0xd415_0932_be9f_feac, - 0xe27b_c7c4_7d44_ee50, - 0x14b6_a78d_3ec7_a560 - }); - - Assert.AreEqual(b, a.Square()); - } - - [TestMethod] - public void TestMultiplication() - { - Fp a = Fp.FromRawUnchecked(new ulong[] - { - 0x0397_a383_2017_0cd4, - 0x734c_1b2c_9e76_1d30, - 0x5ed2_55ad_9a48_beb5, - 0x095a_3c6b_22a7_fcfc, - 0x2294_ce75_d4e2_6a27, - 0x1333_8bd8_7001_1ebb - }); - Fp b = Fp.FromRawUnchecked(new ulong[] - { - 0xb9c3_c7c5_b119_6af7, - 0x2580_e208_6ce3_35c1, - 0xf49a_ed3d_8a57_ef42, - 0x41f2_81e4_9846_e878, - 0xe076_2346_c384_52ce, - 0x0652_e893_26e5_7dc0 - }); - Fp c = Fp.FromRawUnchecked(new ulong[] - { - 0xf96e_f3d7_11ab_5355, - 0xe8d4_59ea_00f1_48dd, - 0x53f7_354a_5f00_fa78, - 0x9e34_a4f3_125c_5f83, - 0x3fbe_0c47_ca74_c19e, - 0x01b0_6a8b_bd4a_dfe4 - }); - - Assert.AreEqual(c, a * b); - } - - [TestMethod] - public void TestAddition() - { - Fp a = Fp.FromRawUnchecked(new ulong[] - { - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b - }); - Fp b = Fp.FromRawUnchecked(new ulong[] - { - 0x9fd2_8773_3d23_dda0, - 0xb16b_f2af_738b_3554, - 0x3e57_a75b_d3cc_6d1d, - 0x900b_c0bd_627f_d6d6, - 0xd319_a080_efb2_45fe, - 0x15fd_caa4_e4bb_2091 - }); - Fp c = Fp.FromRawUnchecked(new ulong[] - { - 0x3934_42cc_b58b_b327, - 0x1092_685f_3bd5_47e3, - 0x3382_252c_ab6a_c4c9, - 0xf946_94cb_7688_7f55, - 0x4b21_5e90_93a5_e071, - 0x0d56_e30f_34f5_f853 - }); - - Assert.AreEqual(c, a + b); - } + static bool IsEqual(in Fp a, in Fp b) + { + bool eq = StructuralComparisons.StructuralEqualityComparer.Equals(a, b); + bool ct_eq = a == b; + Assert.AreEqual(eq, ct_eq); + return eq; + } + + Assert.IsTrue(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 7, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 7, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 7, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 7, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 7, 6 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + Assert.IsFalse(IsEqual(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 7 }), Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }))); + } - [TestMethod] - public void TestSubtraction() - { - Fp a = Fp.FromRawUnchecked(new ulong[] - { - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b - }); - Fp b = Fp.FromRawUnchecked(new ulong[] - { - 0x9fd2_8773_3d23_dda0, - 0xb16b_f2af_738b_3554, - 0x3e57_a75b_d3cc_6d1d, - 0x900b_c0bd_627f_d6d6, - 0xd319_a080_efb2_45fe, - 0x15fd_caa4_e4bb_2091 - }); - Fp c = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestConditionalSelection() { - 0x6d8d_33e6_3b43_4d3d, - 0xeb12_82fd_b766_dd39, - 0x8534_7bb6_f133_d6d5, - 0xa21d_aa5a_9892_f727, - 0x3b25_6cfb_3ad8_ae23, - 0x155d_7199_de7f_8464 - }); + Fp a = Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }); + Fp b = Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 }); - Assert.AreEqual(c, a - b); - } + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } - [TestMethod] - public void TestNegation() - { - Fp a = Fp.FromRawUnchecked(new ulong[] - { - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b - }); - Fp b = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestSquaring() { - 0x669e_44a6_8798_2a79, - 0xa0d9_8a50_37b5_ed71, - 0x0ad5_822f_2861_a854, - 0x96c5_2bf1_ebf7_5781, - 0x87f8_41f0_5c0c_658c, - 0x08a6_e795_afc5_283e - }); - - Assert.AreEqual(b, -a); - } + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0xd215_d276_8e83_191b, + 0x5085_d80f_8fb2_8261, + 0xce9a_032d_df39_3a56, + 0x3e9c_4fff_2ca0_c4bb, + 0x6436_b6f7_f4d9_5dfb, + 0x1060_6628_ad4a_4d90 + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x33d9_c42a_3cb3_e235, + 0xdad1_1a09_4c4c_d455, + 0xa2f1_44bd_729a_aeba, + 0xd415_0932_be9f_feac, + 0xe27b_c7c4_7d44_ee50, + 0x14b6_a78d_3ec7_a560 + }); + + Assert.AreEqual(b, a.Square()); + } - [TestMethod] - public void TestToString() - { - Fp a = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestMultiplication() { - 0x5360_bb59_7867_8032, - 0x7dd2_75ae_799e_128e, - 0x5c5b_5071_ce4f_4dcf, - 0xcdb2_1f93_078d_bb3e, - 0xc323_65c5_e73f_474a, - 0x115a_2a54_89ba_be5b - }); - - Assert.AreEqual("0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704", a.ToString()); - } + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x0397_a383_2017_0cd4, + 0x734c_1b2c_9e76_1d30, + 0x5ed2_55ad_9a48_beb5, + 0x095a_3c6b_22a7_fcfc, + 0x2294_ce75_d4e2_6a27, + 0x1333_8bd8_7001_1ebb + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0xb9c3_c7c5_b119_6af7, + 0x2580_e208_6ce3_35c1, + 0xf49a_ed3d_8a57_ef42, + 0x41f2_81e4_9846_e878, + 0xe076_2346_c384_52ce, + 0x0652_e893_26e5_7dc0 + }); + Fp c = Fp.FromRawUnchecked(new ulong[] + { + 0xf96e_f3d7_11ab_5355, + 0xe8d4_59ea_00f1_48dd, + 0x53f7_354a_5f00_fa78, + 0x9e34_a4f3_125c_5f83, + 0x3fbe_0c47_ca74_c19e, + 0x01b0_6a8b_bd4a_dfe4 + }); + + Assert.AreEqual(c, a * b); + } - [TestMethod] - public void TestConstructor() - { - Fp a = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestAddition() { - 0xdc90_6d9b_e3f9_5dc8, - 0x8755_caf7_4596_91a1, - 0xcff1_a7f4_e958_3ab3, - 0x9b43_821f_849e_2284, - 0xf575_54f3_a297_4f3f, - 0x085d_bea8_4ed4_7f79 - }); + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091 + }); + Fp c = Fp.FromRawUnchecked(new ulong[] + { + 0x3934_42cc_b58b_b327, + 0x1092_685f_3bd5_47e3, + 0x3382_252c_ab6a_c4c9, + 0xf946_94cb_7688_7f55, + 0x4b21_5e90_93a5_e071, + 0x0d56_e30f_34f5_f853 + }); + + Assert.AreEqual(c, a + b); + } - for (int i = 0; i < 100; i++) + [TestMethod] + public void TestSubtraction() { - a = a.Square(); - byte[] tmp = a.ToArray(); - Fp b = Fp.FromBytes(tmp); - - Assert.AreEqual(b, a); + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x9fd2_8773_3d23_dda0, + 0xb16b_f2af_738b_3554, + 0x3e57_a75b_d3cc_6d1d, + 0x900b_c0bd_627f_d6d6, + 0xd319_a080_efb2_45fe, + 0x15fd_caa4_e4bb_2091 + }); + Fp c = Fp.FromRawUnchecked(new ulong[] + { + 0x6d8d_33e6_3b43_4d3d, + 0xeb12_82fd_b766_dd39, + 0x8534_7bb6_f133_d6d5, + 0xa21d_aa5a_9892_f727, + 0x3b25_6cfb_3ad8_ae23, + 0x155d_7199_de7f_8464 + }); + + Assert.AreEqual(c, a - b); } - Assert.AreEqual(-Fp.One, Fp.FromBytes(new byte[] + [TestMethod] + public void TestNegation() { - 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, - 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, - 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - })); + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x669e_44a6_8798_2a79, + 0xa0d9_8a50_37b5_ed71, + 0x0ad5_822f_2861_a854, + 0x96c5_2bf1_ebf7_5781, + 0x87f8_41f0_5c0c_658c, + 0x08a6_e795_afc5_283e + }); + + Assert.AreEqual(b, -a); + } - Assert.ThrowsException(() => Fp.FromBytes(new byte[] + [TestMethod] + public void TestToString() { - 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, - 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, - 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 - })); - - Assert.ThrowsException(() => Fp.FromBytes(Enumerable.Repeat(0xff, 48).ToArray())); - } + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x5360_bb59_7867_8032, + 0x7dd2_75ae_799e_128e, + 0x5c5b_5071_ce4f_4dcf, + 0xcdb2_1f93_078d_bb3e, + 0xc323_65c5_e73f_474a, + 0x115a_2a54_89ba_be5b + }); + + Assert.AreEqual("0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704", a.ToString()); + } - [TestMethod] - public void TestSqrt() - { - // a = 4 - Fp a = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestConstructor() { - 0xaa27_0000_000c_fff3, - 0x53cc_0032_fc34_000a, - 0x478f_e97a_6b0a_807f, - 0xb1d3_7ebe_e6ba_24d7, - 0x8ec9_733b_bf78_ab2f, - 0x09d6_4551_3d83_de7e - }); + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0xdc90_6d9b_e3f9_5dc8, + 0x8755_caf7_4596_91a1, + 0xcff1_a7f4_e958_3ab3, + 0x9b43_821f_849e_2284, + 0xf575_54f3_a297_4f3f, + 0x085d_bea8_4ed4_7f79 + }); + + for (int i = 0; i < 100; i++) + { + a = a.Square(); + byte[] tmp = a.ToArray(); + Fp b = Fp.FromBytes(tmp); + + Assert.AreEqual(b, a); + } + + Assert.AreEqual(-Fp.One, Fp.FromBytes(new byte[] + { + 26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + })); + + Assert.ThrowsException(() => Fp.FromBytes(new byte[] + { + 27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75, + 132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177, + 83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170 + })); + + Assert.ThrowsException(() => Fp.FromBytes(Enumerable.Repeat(0xff, 48).ToArray())); + } - // b = 2 - Fp b = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestSqrt() { - 0x3213_0000_0006_554f, - 0xb93c_0018_d6c4_0005, - 0x5760_5e0d_b0dd_bb51, - 0x8b25_6521_ed1f_9bcb, - 0x6cf2_8d79_0162_2c03, - 0x11eb_ab9d_bb81_e28c - }); - - // sqrt(4) = -2 - Assert.AreEqual(b, -a.Sqrt()); - } + // a = 4 + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0xaa27_0000_000c_fff3, + 0x53cc_0032_fc34_000a, + 0x478f_e97a_6b0a_807f, + 0xb1d3_7ebe_e6ba_24d7, + 0x8ec9_733b_bf78_ab2f, + 0x09d6_4551_3d83_de7e + }); + + // b = 2 + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x3213_0000_0006_554f, + 0xb93c_0018_d6c4_0005, + 0x5760_5e0d_b0dd_bb51, + 0x8b25_6521_ed1f_9bcb, + 0x6cf2_8d79_0162_2c03, + 0x11eb_ab9d_bb81_e28c + }); + + // sqrt(4) = -2 + Assert.AreEqual(b, -a.Sqrt()); + } - [TestMethod] - public void TestInversion() - { - Fp a = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestInversion() { - 0x43b4_3a50_78ac_2076, - 0x1ce0_7630_46f8_962b, - 0x724a_5276_486d_735c, - 0x6f05_c2a6_282d_48fd, - 0x2095_bd5b_b4ca_9331, - 0x03b3_5b38_94b0_f7da - }); - Fp b = Fp.FromRawUnchecked(new ulong[] - { - 0x69ec_d704_0952_148f, - 0x985c_cc20_2219_0f55, - 0xe19b_ba36_a9ad_2f41, - 0x19bb_16c9_5219_dbd8, - 0x14dc_acfd_fb47_8693, - 0x115f_f58a_fff9_a8e1 - }); - - Assert.AreEqual(b, a.Invert()); - Assert.ThrowsException(() => Fp.Zero.Invert()); - } + Fp a = Fp.FromRawUnchecked(new ulong[] + { + 0x43b4_3a50_78ac_2076, + 0x1ce0_7630_46f8_962b, + 0x724a_5276_486d_735c, + 0x6f05_c2a6_282d_48fd, + 0x2095_bd5b_b4ca_9331, + 0x03b3_5b38_94b0_f7da + }); + Fp b = Fp.FromRawUnchecked(new ulong[] + { + 0x69ec_d704_0952_148f, + 0x985c_cc20_2219_0f55, + 0xe19b_ba36_a9ad_2f41, + 0x19bb_16c9_5219_dbd8, + 0x14dc_acfd_fb47_8693, + 0x115f_f58a_fff9_a8e1 + }); + + Assert.AreEqual(b, a.Invert()); + Assert.ThrowsException(() => Fp.Zero.Invert()); + } - [TestMethod] - public void TestLexicographicLargest() - { - Assert.IsFalse(Fp.Zero.LexicographicallyLargest()); - Assert.IsFalse(Fp.One.LexicographicallyLargest()); - Assert.IsFalse(Fp.FromRawUnchecked(new ulong[] - { - 0xa1fa_ffff_fffe_5557, - 0x995b_fff9_76a3_fffe, - 0x03f4_1d24_d174_ceb4, - 0xf654_7998_c199_5dbd, - 0x778a_468f_507a_6034, - 0x0205_5993_1f7f_8103 - }).LexicographicallyLargest()); - Assert.IsTrue(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestLexicographicLargest() { - 0x1804_0000_0001_5554, - 0x8550_0005_3ab0_0001, - 0x633c_b57c_253c_276f, - 0x6e22_d1ec_31eb_b502, - 0xd391_6126_f2d1_4ca2, - 0x17fb_b857_1a00_6596 - }).LexicographicallyLargest()); - Assert.IsTrue(Fp.FromRawUnchecked(new ulong[] - { - 0x43f5_ffff_fffc_aaae, - 0x32b7_fff2_ed47_fffd, - 0x07e8_3a49_a2e9_9d69, - 0xeca8_f331_8332_bb7a, - 0xef14_8d1e_a0f4_c069, - 0x040a_b326_3eff_0206 - }).LexicographicallyLargest()); + Assert.IsFalse(Fp.Zero.LexicographicallyLargest()); + Assert.IsFalse(Fp.One.LexicographicallyLargest()); + Assert.IsFalse(Fp.FromRawUnchecked(new ulong[] + { + 0xa1fa_ffff_fffe_5557, + 0x995b_fff9_76a3_fffe, + 0x03f4_1d24_d174_ceb4, + 0xf654_7998_c199_5dbd, + 0x778a_468f_507a_6034, + 0x0205_5993_1f7f_8103 + }).LexicographicallyLargest()); + Assert.IsTrue(Fp.FromRawUnchecked(new ulong[] + { + 0x1804_0000_0001_5554, + 0x8550_0005_3ab0_0001, + 0x633c_b57c_253c_276f, + 0x6e22_d1ec_31eb_b502, + 0xd391_6126_f2d1_4ca2, + 0x17fb_b857_1a00_6596 + }).LexicographicallyLargest()); + Assert.IsTrue(Fp.FromRawUnchecked(new ulong[] + { + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206 + }).LexicographicallyLargest()); + } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs index 238a20c6c6..a3c6d62054 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp12.cs @@ -9,339 +9,340 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_Fp12 +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestArithmetic() + [TestClass] + public class UT_Fp12 { - var a = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040 - }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040 - })))); + [TestMethod] + public void TestArithmetic() + { + var a = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + })))); - var b = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d272_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e348 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040 - }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd2_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a117_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040 - })))); + var b = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d272_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e348 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd2_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a117_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + })))); - var c = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_71b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0x7791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_133c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_40e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_1744_c040 - }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d3_c010_e60f, - 0x0acd_b8e1_58bf_e3c8 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_1040 - })))); + var c = new Fp12(new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_71b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0x7791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_133c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_40e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_1744_c040 + }))), new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d3_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_1040 + })))); - // because a and b and c are similar to each other and - // I was lazy, this is just some arbitrary way to make - // them a little more different - a = a.Square().Invert().Square() + c; - b = b.Square().Invert().Square() + a; - c = c.Square().Invert().Square() + b; + // because a and b and c are similar to each other and + // I was lazy, this is just some arbitrary way to make + // them a little more different + a = a.Square().Invert().Square() + c; + b = b.Square().Invert().Square() + a; + c = c.Square().Invert().Square() + b; - Assert.AreEqual(a * a, a.Square()); - Assert.AreEqual(b * b, b.Square()); - Assert.AreEqual(c * c, c.Square()); + Assert.AreEqual(a * a, a.Square()); + Assert.AreEqual(b * b, b.Square()); + Assert.AreEqual(c * c, c.Square()); - Assert.AreEqual((a + b) * c.Square(), (c * c * a) + (c * c * b)); + Assert.AreEqual((a + b) * c.Square(), (c * c * a) + (c * c * b)); - Assert.AreEqual(a.Invert() * b.Invert(), (a * b).Invert()); - Assert.AreEqual(Fp12.One, a.Invert() * a); + Assert.AreEqual(a.Invert() * b.Invert(), (a * b).Invert()); + Assert.AreEqual(Fp12.One, a.Invert() * a); - Assert.AreNotEqual(a, a.FrobeniusMap()); - Assert.AreEqual( - a, - a.FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - .FrobeniusMap() - ); + Assert.AreNotEqual(a, a.FrobeniusMap()); + Assert.AreEqual( + a, + a.FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + .FrobeniusMap() + ); + } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs index 8e8781b834..3021f16507 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp2.cs @@ -12,482 +12,483 @@ using System.Collections; using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_Fp2 +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestConditionalSelection() + [TestClass] + public class UT_Fp2 { - var a = new Fp2( - Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), - Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 }) - ); - var b = new Fp2( - Fp.FromRawUnchecked(new ulong[] { 13, 14, 15, 16, 17, 18 }), - Fp.FromRawUnchecked(new ulong[] { 19, 20, 21, 22, 23, 24 }) - ); + [TestMethod] + public void TestConditionalSelection() + { + var a = new Fp2( + Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), + Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 }) + ); + var b = new Fp2( + Fp.FromRawUnchecked(new ulong[] { 13, 14, 15, 16, 17, 18 }), + Fp.FromRawUnchecked(new ulong[] { 19, 20, 21, 22, 23, 24 }) + ); - Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); - Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); - } + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } - [TestMethod] - public void TestEquality() - { - static bool IsEqual(in Fp2 a, in Fp2 b) + [TestMethod] + public void TestEquality() { - var eq = StructuralComparisons.StructuralEqualityComparer.Equals(a, b); - var ct_eq = a == b; - Assert.AreEqual(eq, ct_eq); - return eq; - } + static bool IsEqual(in Fp2 a, in Fp2 b) + { + var eq = StructuralComparisons.StructuralEqualityComparer.Equals(a, b); + var ct_eq = a == b; + Assert.AreEqual(eq, ct_eq); + return eq; + } - Assert.IsTrue(IsEqual( - new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })), - new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) - )); + Assert.IsTrue(IsEqual( + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })), + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) + )); - Assert.IsFalse(IsEqual( - new Fp2(Fp.FromRawUnchecked(new ulong[] { 2, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })), - new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) - )); + Assert.IsFalse(IsEqual( + new Fp2(Fp.FromRawUnchecked(new ulong[] { 2, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })), + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) + )); - Assert.IsFalse(IsEqual( - new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 2, 8, 9, 10, 11, 12 })), - new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) - )); - } + Assert.IsFalse(IsEqual( + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 2, 8, 9, 10, 11, 12 })), + new Fp2(Fp.FromRawUnchecked(new ulong[] { 1, 2, 3, 4, 5, 6 }), Fp.FromRawUnchecked(new ulong[] { 7, 8, 9, 10, 11, 12 })) + )); + } - [TestMethod] - public void TestSquaring() - { - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestSquaring() { - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - })); - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - }), Fp.FromRawUnchecked(new ulong[] - { - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - })); + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); - Assert.AreEqual(a.Square(), b); - } + Assert.AreEqual(a.Square(), b); + } - [TestMethod] - public void TestMultiplication() - { - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestMultiplication() { - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - })); - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - }), Fp.FromRawUnchecked(new ulong[] - { - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - })); - var c = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xf597_483e_27b4_e0f7, - 0x610f_badf_811d_ae5f, - 0x8432_af91_7714_327a, - 0x6a9a_9603_cf88_f09e, - 0xf05a_7bf8_bad0_eb01, - 0x0954_9131_c003_ffae, - }), Fp.FromRawUnchecked(new ulong[] - { - 0x963b_02d0_f93d_37cd, - 0xc95c_e1cd_b30a_73d4, - 0x3087_25fa_3126_f9b8, - 0x56da_3c16_7fab_0d50, - 0x6b50_86b5_f4b6_d6af, - 0x09c3_9f06_2f18_e9f2, - })); + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xf597_483e_27b4_e0f7, + 0x610f_badf_811d_ae5f, + 0x8432_af91_7714_327a, + 0x6a9a_9603_cf88_f09e, + 0xf05a_7bf8_bad0_eb01, + 0x0954_9131_c003_ffae, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x963b_02d0_f93d_37cd, + 0xc95c_e1cd_b30a_73d4, + 0x3087_25fa_3126_f9b8, + 0x56da_3c16_7fab_0d50, + 0x6b50_86b5_f4b6_d6af, + 0x09c3_9f06_2f18_e9f2, + })); - Assert.AreEqual(c, a * b); - } + Assert.AreEqual(c, a * b); + } - [TestMethod] - public void TestAddition() - { - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestAddition() { - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - }), Fp.FromRawUnchecked(new ulong[] - { - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - })); - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - }), Fp.FromRawUnchecked(new ulong[] - { - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - })); - var c = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x6b82_a9a7_08c1_32d2, - 0x476b_1da3_39ba_5ba4, - 0x848c_0e62_4b91_cd87, - 0x11f9_5955_295a_99ec, - 0xf337_6fce_2255_9f06, - 0x0c3f_e3fa_ce8c_8f43, - }), Fp.FromRawUnchecked(new ulong[] - { - 0x6f99_2c12_73ab_5bc5, - 0x3355_1366_17a1_df33, - 0x8b0e_f74c_0aed_aff9, - 0x062f_9246_8ad2_ca12, - 0xe146_9770_738f_d584, - 0x12c3_c3dd_84bc_a26d, - })); + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x6b82_a9a7_08c1_32d2, + 0x476b_1da3_39ba_5ba4, + 0x848c_0e62_4b91_cd87, + 0x11f9_5955_295a_99ec, + 0xf337_6fce_2255_9f06, + 0x0c3f_e3fa_ce8c_8f43, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x6f99_2c12_73ab_5bc5, + 0x3355_1366_17a1_df33, + 0x8b0e_f74c_0aed_aff9, + 0x062f_9246_8ad2_ca12, + 0xe146_9770_738f_d584, + 0x12c3_c3dd_84bc_a26d, + })); - Assert.AreEqual(a + b, c); - } + Assert.AreEqual(a + b, c); + } - [TestMethod] - public void TestSubtraction() - { - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - }), Fp.FromRawUnchecked(new ulong[] - { - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - })); - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xa1e0_9175_a4d2_c1fe, - 0x8b33_acfc_204e_ff12, - 0xe244_15a1_1b45_6e42, - 0x61d9_96b1_b6ee_1936, - 0x1164_dbe8_667c_853c, - 0x0788_557a_cc7d_9c79, - }), Fp.FromRawUnchecked(new ulong[] - { - 0xda6a_87cc_6f48_fa36, - 0x0fc7_b488_277c_1903, - 0x9445_ac4a_dc44_8187, - 0x0261_6d5b_c909_9209, - 0xdbed_4677_2db5_8d48, - 0x11b9_4d50_76c7_b7b1, - })); - var c = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xe1c0_86bb_bf1b_5981, - 0x4faf_c3a9_aa70_5d7e, - 0x2734_b5c1_0bb7_e726, - 0xb2bd_7776_af03_7a3e, - 0x1b89_5fb3_98a8_4164, - 0x1730_4aef_6f11_3cec, - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestSubtraction() { - 0x74c3_1c79_9519_1204, - 0x3271_aa54_79fd_ad2b, - 0xc9b4_7157_4915_a30f, - 0x65e4_0313_ec44_b8be, - 0x7487_b238_5b70_67cb, - 0x0952_3b26_d0ad_19a4, - })); + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xa1e0_9175_a4d2_c1fe, + 0x8b33_acfc_204e_ff12, + 0xe244_15a1_1b45_6e42, + 0x61d9_96b1_b6ee_1936, + 0x1164_dbe8_667c_853c, + 0x0788_557a_cc7d_9c79, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xda6a_87cc_6f48_fa36, + 0x0fc7_b488_277c_1903, + 0x9445_ac4a_dc44_8187, + 0x0261_6d5b_c909_9209, + 0xdbed_4677_2db5_8d48, + 0x11b9_4d50_76c7_b7b1, + })); + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xe1c0_86bb_bf1b_5981, + 0x4faf_c3a9_aa70_5d7e, + 0x2734_b5c1_0bb7_e726, + 0xb2bd_7776_af03_7a3e, + 0x1b89_5fb3_98a8_4164, + 0x1730_4aef_6f11_3cec, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x74c3_1c79_9519_1204, + 0x3271_aa54_79fd_ad2b, + 0xc9b4_7157_4915_a30f, + 0x65e4_0313_ec44_b8be, + 0x7487_b238_5b70_67cb, + 0x0952_3b26_d0ad_19a4, + })); - Assert.AreEqual(c, a - b); - } + Assert.AreEqual(c, a - b); + } - [TestMethod] - public void TestNegation() - { - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xc9a2_1831_63ee_70d4, - 0xbc37_70a7_196b_5c91, - 0xa247_f8c1_304c_5f44, - 0xb01f_c2a3_726c_80b5, - 0xe1d2_93e5_bbd9_19c9, - 0x04b7_8e80_020e_f2ca, - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestNegation() { - 0x952e_a446_0462_618f, - 0x238d_5edd_f025_c62f, - 0xf6c9_4b01_2ea9_2e72, - 0x03ce_24ea_c1c9_3808, - 0x0559_50f9_45da_483c, - 0x010a_768d_0df4_eabc, - })); - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xf05c_e7ce_9c11_39d7, - 0x6274_8f57_97e8_a36d, - 0xc4e8_d9df_c664_96df, - 0xb457_88e1_8118_9209, - 0x6949_13d0_8772_930d, - 0x1549_836a_3770_f3cf, - }), Fp.FromRawUnchecked(new ulong[] - { - 0x24d0_5bb9_fb9d_491c, - 0xfb1e_a120_c12e_39d0, - 0x7067_879f_c807_c7b1, - 0x60a9_269a_31bb_dab6, - 0x45c2_56bc_fd71_649b, - 0x18f6_9b5d_2b8a_fbde, - })); + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc9a2_1831_63ee_70d4, + 0xbc37_70a7_196b_5c91, + 0xa247_f8c1_304c_5f44, + 0xb01f_c2a3_726c_80b5, + 0xe1d2_93e5_bbd9_19c9, + 0x04b7_8e80_020e_f2ca, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x952e_a446_0462_618f, + 0x238d_5edd_f025_c62f, + 0xf6c9_4b01_2ea9_2e72, + 0x03ce_24ea_c1c9_3808, + 0x0559_50f9_45da_483c, + 0x010a_768d_0df4_eabc, + })); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xf05c_e7ce_9c11_39d7, + 0x6274_8f57_97e8_a36d, + 0xc4e8_d9df_c664_96df, + 0xb457_88e1_8118_9209, + 0x6949_13d0_8772_930d, + 0x1549_836a_3770_f3cf, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x24d0_5bb9_fb9d_491c, + 0xfb1e_a120_c12e_39d0, + 0x7067_879f_c807_c7b1, + 0x60a9_269a_31bb_dab6, + 0x45c2_56bc_fd71_649b, + 0x18f6_9b5d_2b8a_fbde, + })); - Assert.AreEqual(b, -a); - } + Assert.AreEqual(b, -a); + } - [TestMethod] - public void TestSqrt() - { - // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x2bee_d146_27d7_f9e9, - 0xb661_4e06_660e_5dce, - 0x06c4_cc7c_2f91_d42c, - 0x996d_7847_4b7a_63cc, - 0xebae_bc4c_820d_574e, - 0x1886_5e12_d93f_d845, - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestSqrt() { - 0x7d82_8664_baf4_f566, - 0xd17e_6639_96ec_7339, - 0x679e_ad55_cb40_78d0, - 0xfe3b_2260_e001_ec28, - 0x3059_93d0_43d9_1b68, - 0x0626_f03c_0489_b72d, - })); + // a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295 + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x2bee_d146_27d7_f9e9, + 0xb661_4e06_660e_5dce, + 0x06c4_cc7c_2f91_d42c, + 0x996d_7847_4b7a_63cc, + 0xebae_bc4c_820d_574e, + 0x1886_5e12_d93f_d845, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7d82_8664_baf4_f566, + 0xd17e_6639_96ec_7339, + 0x679e_ad55_cb40_78d0, + 0xfe3b_2260_e001_ec28, + 0x3059_93d0_43d9_1b68, + 0x0626_f03c_0489_b72d, + })); - Assert.AreEqual(a, a.Sqrt().Square()); + Assert.AreEqual(a, a.Sqrt().Square()); - // b = 5, which is a generator of the p - 1 order - // multiplicative subgroup - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x6631_0000_0010_5545, - 0x2114_0040_0eec_000d, - 0x3fa7_af30_c820_e316, - 0xc52a_8b8d_6387_695d, - 0x9fb4_e61d_1e83_eac5, - 0x005c_b922_afe8_4dc7, - }), Fp.Zero); + // b = 5, which is a generator of the p - 1 order + // multiplicative subgroup + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x6631_0000_0010_5545, + 0x2114_0040_0eec_000d, + 0x3fa7_af30_c820_e316, + 0xc52a_8b8d_6387_695d, + 0x9fb4_e61d_1e83_eac5, + 0x005c_b922_afe8_4dc7, + }), Fp.Zero); - Assert.AreEqual(b, b.Sqrt().Square()); + Assert.AreEqual(b, b.Sqrt().Square()); - // c = 25, which is a generator of the (p - 1) / 2 order - // multiplicative subgroup - var c = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x44f6_0000_0051_ffae, - 0x86b8_0141_9948_0043, - 0xd715_9952_f1f3_794a, - 0x755d_6e3d_fe1f_fc12, - 0xd36c_d6db_5547_e905, - 0x02f8_c8ec_bf18_67bb, - }), Fp.Zero); + // c = 25, which is a generator of the (p - 1) / 2 order + // multiplicative subgroup + var c = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x44f6_0000_0051_ffae, + 0x86b8_0141_9948_0043, + 0xd715_9952_f1f3_794a, + 0x755d_6e3d_fe1f_fc12, + 0xd36c_d6db_5547_e905, + 0x02f8_c8ec_bf18_67bb, + }), Fp.Zero); - Assert.AreEqual(c, c.Sqrt().Square()); + Assert.AreEqual(c, c.Sqrt().Square()); - // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 - // is nonsquare. - Assert.ThrowsException(() => - new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xc5fa_1bc8_fd00_d7f6, - 0x3830_ca45_4606_003b, - 0x2b28_7f11_04b1_02da, - 0xa7fb_30f2_8230_f23e, - 0x339c_db9e_e953_dbf0, - 0x0d78_ec51_d989_fc57, - }), Fp.FromRawUnchecked(new ulong[]{ - 0x27ec_4898_cf87_f613, - 0x9de1_394e_1abb_05a5, - 0x0947_f85d_c170_fc14, - 0x586f_bc69_6b61_14b7, - 0x2b34_75a4_077d_7169, - 0x13e1_c895_cc4b_6c22, - })).Sqrt()); - } + // 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529 + // is nonsquare. + Assert.ThrowsException(() => + new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xc5fa_1bc8_fd00_d7f6, + 0x3830_ca45_4606_003b, + 0x2b28_7f11_04b1_02da, + 0xa7fb_30f2_8230_f23e, + 0x339c_db9e_e953_dbf0, + 0x0d78_ec51_d989_fc57, + }), Fp.FromRawUnchecked(new ulong[]{ + 0x27ec_4898_cf87_f613, + 0x9de1_394e_1abb_05a5, + 0x0947_f85d_c170_fc14, + 0x586f_bc69_6b61_14b7, + 0x2b34_75a4_077d_7169, + 0x13e1_c895_cc4b_6c22, + })).Sqrt()); + } - [TestMethod] - public void TestInversion() - { - var a = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestInversion() { - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - })); + var a = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + })); - var b = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x0581_a133_3d4f_48a6, - 0x5824_2f6e_f074_8500, - 0x0292_c955_349e_6da5, - 0xba37_721d_dd95_fcd0, - 0x70d1_6790_3aa5_dfc5, - 0x1189_5e11_8b58_a9d5, - }), Fp.FromRawUnchecked(new ulong[] - { - 0x0eda_09d2_d7a8_5d17, - 0x8808_e137_a7d1_a2cf, - 0x43ae_2625_c1ff_21db, - 0xf85a_c9fd_f7a7_4c64, - 0x8fcc_dda5_b8da_9738, - 0x08e8_4f0c_b32c_d17d, - })); - Assert.AreEqual(b, a.Invert()); + var b = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x0581_a133_3d4f_48a6, + 0x5824_2f6e_f074_8500, + 0x0292_c955_349e_6da5, + 0xba37_721d_dd95_fcd0, + 0x70d1_6790_3aa5_dfc5, + 0x1189_5e11_8b58_a9d5, + }), Fp.FromRawUnchecked(new ulong[] + { + 0x0eda_09d2_d7a8_5d17, + 0x8808_e137_a7d1_a2cf, + 0x43ae_2625_c1ff_21db, + 0xf85a_c9fd_f7a7_4c64, + 0x8fcc_dda5_b8da_9738, + 0x08e8_4f0c_b32c_d17d, + })); + Assert.AreEqual(b, a.Invert()); - Assert.ThrowsException(() => Fp2.Zero.Invert()); - } + Assert.ThrowsException(() => Fp2.Zero.Invert()); + } - [TestMethod] - public void TestLexicographicLargest() - { - Assert.IsFalse(Fp2.Zero.LexicographicallyLargest()); - Assert.IsFalse(Fp2.One.LexicographicallyLargest()); - Assert.IsTrue(new Fp2(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestLexicographicLargest() { - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - }), Fp.FromRawUnchecked(new ulong[] - { - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - })).LexicographicallyLargest()); - Assert.IsFalse(new Fp2(-Fp.FromRawUnchecked(new ulong[] - { - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - }), -Fp.FromRawUnchecked(new ulong[] - { - 0xd328_e37c_c2f5_8d41, - 0x948d_f085_8a60_5869, - 0x6032_f9d5_6f93_a573, - 0x2be4_83ef_3fff_dc87, - 0x30ef_61f8_8f48_3c2a, - 0x1333_f55a_3572_5be0, - })).LexicographicallyLargest()); - Assert.IsFalse(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - }), Fp.Zero).LexicographicallyLargest()); - Assert.IsTrue(new Fp2(-Fp.FromRawUnchecked(new ulong[] - { - 0x1128_ecad_6754_9455, - 0x9e7a_1cff_3a4e_a1a8, - 0xeb20_8d51_e08b_cf27, - 0xe98a_d408_11f5_fc2b, - 0x736c_3a59_232d_511d, - 0x10ac_d42d_29cf_cbb6, - }), Fp.Zero).LexicographicallyLargest()); + Assert.IsFalse(Fp2.Zero.LexicographicallyLargest()); + Assert.IsFalse(Fp2.One.LexicographicallyLargest()); + Assert.IsTrue(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + })).LexicographicallyLargest()); + Assert.IsFalse(new Fp2(-Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), -Fp.FromRawUnchecked(new ulong[] + { + 0xd328_e37c_c2f5_8d41, + 0x948d_f085_8a60_5869, + 0x6032_f9d5_6f93_a573, + 0x2be4_83ef_3fff_dc87, + 0x30ef_61f8_8f48_3c2a, + 0x1333_f55a_3572_5be0, + })).LexicographicallyLargest()); + Assert.IsFalse(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.Zero).LexicographicallyLargest()); + Assert.IsTrue(new Fp2(-Fp.FromRawUnchecked(new ulong[] + { + 0x1128_ecad_6754_9455, + 0x9e7a_1cff_3a4e_a1a8, + 0xeb20_8d51_e08b_cf27, + 0xe98a_d408_11f5_fc2b, + 0x736c_3a59_232d_511d, + 0x10ac_d42d_29cf_cbb6, + }), Fp.Zero).LexicographicallyLargest()); + } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs index 6fcc63fc97..eb0115506e 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Fp6.cs @@ -9,171 +9,172 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_Fp6 +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestArithmetic() + [TestClass] + public class UT_Fp6 { - var a = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x47f9_cb98_b1b8_2d58, - 0x5fe9_11eb_a3aa_1d9d, - 0x96bf_1b5f_4dd8_1db3, - 0x8100_d27c_c925_9f5b, - 0xafa2_0b96_7464_0eab, - 0x09bb_cea7_d8d9_497d - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0x0303_cb98_b166_2daa, - 0xd931_10aa_0a62_1d5a, - 0xbfa9_820c_5be4_a468, - 0x0ba3_643e_cb05_a348, - 0xdc35_34bb_1f1c_25a6, - 0x06c3_05bb_19c0_e1c1 - })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x46f9_cb98_b162_d858, - 0x0be9_109c_f7aa_1d57, - 0xc791_bc55_fece_41d2, - 0xf84c_5770_4e38_5ec2, - 0xcb49_c1d9_c010_e60f, - 0x0acd_b8e1_58bf_e3c8 - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0x8aef_cb98_b15f_8306, - 0x3ea1_108f_e4f2_1d54, - 0xcf79_f69f_a1b7_df3b, - 0xe4f5_4aa1_d16b_1a3c, - 0xba5e_4ef8_6105_a679, - 0x0ed8_6c07_97be_e5cf - })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcee5_cb98_b15c_2db4, - 0x7159_1082_d23a_1d51, - 0xd762_30e9_44a1_7ca4, - 0xd19e_3dd3_549d_d5b6, - 0xa972_dc17_01fa_66e3, - 0x12e3_1f2d_d6bd_e7d6 - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0xad2a_cb98_b173_2d9d, - 0x2cfd_10dd_0696_1d64, - 0x0739_6b86_c6ef_24e8, - 0xbd76_e2fd_b1bf_c820, - 0x6afe_a7f6_de94_d0d5, - 0x1099_4b0c_5744_c040 - }))); + [TestMethod] + public void TestArithmetic() + { + var a = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x47f9_cb98_b1b8_2d58, + 0x5fe9_11eb_a3aa_1d9d, + 0x96bf_1b5f_4dd8_1db3, + 0x8100_d27c_c925_9f5b, + 0xafa2_0b96_7464_0eab, + 0x09bb_cea7_d8d9_497d + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x0303_cb98_b166_2daa, + 0xd931_10aa_0a62_1d5a, + 0xbfa9_820c_5be4_a468, + 0x0ba3_643e_cb05_a348, + 0xdc35_34bb_1f1c_25a6, + 0x06c3_05bb_19c0_e1c1 + })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x46f9_cb98_b162_d858, + 0x0be9_109c_f7aa_1d57, + 0xc791_bc55_fece_41d2, + 0xf84c_5770_4e38_5ec2, + 0xcb49_c1d9_c010_e60f, + 0x0acd_b8e1_58bf_e3c8 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x8aef_cb98_b15f_8306, + 0x3ea1_108f_e4f2_1d54, + 0xcf79_f69f_a1b7_df3b, + 0xe4f5_4aa1_d16b_1a3c, + 0xba5e_4ef8_6105_a679, + 0x0ed8_6c07_97be_e5cf + })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcee5_cb98_b15c_2db4, + 0x7159_1082_d23a_1d51, + 0xd762_30e9_44a1_7ca4, + 0xd19e_3dd3_549d_d5b6, + 0xa972_dc17_01fa_66e3, + 0x12e3_1f2d_d6bd_e7d6 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0xad2a_cb98_b173_2d9d, + 0x2cfd_10dd_0696_1d64, + 0x0739_6b86_c6ef_24e8, + 0xbd76_e2fd_b1bf_c820, + 0x6afe_a7f6_de94_d0d5, + 0x1099_4b0c_5744_c040 + }))); - var b = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xf120_cb98_b16f_d84b, - 0x5fb5_10cf_f3de_1d61, - 0x0f21_a5d0_69d8_c251, - 0xaa1f_d62f_34f2_839a, - 0x5a13_3515_7f89_913f, - 0x14a3_fe32_9643_c247 - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0x3516_cb98_b16c_82f9, - 0x926d_10c2_e126_1d5f, - 0x1709_e01a_0cc2_5fba, - 0x96c8_c960_b825_3f14, - 0x4927_c234_207e_51a9, - 0x18ae_b158_d542_c44e - })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xbf0d_cb98_b169_82fc, - 0xa679_10b7_1d1a_1d5c, - 0xb7c1_47c2_b8fb_06ff, - 0x1efa_710d_47d2_e7ce, - 0xed20_a79c_7e27_653c, - 0x02b8_5294_dac1_dfba - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0x9d52_cb98_b180_82e5, - 0x621d_1111_5176_1d6f, - 0xe798_8260_3b48_af43, - 0x0ad3_1637_a4f4_da37, - 0xaeac_737c_5ac1_cf2e, - 0x006e_7e73_5b48_b824 - })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xe148_cb98_b17d_2d93, - 0x94d5_1104_3ebe_1d6c, - 0xef80_bca9_de32_4cac, - 0xf77c_0969_2827_95b1, - 0x9dc1_009a_fbb6_8f97, - 0x0479_3199_9a47_ba2b - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0x253e_cb98_b179_d841, - 0xc78d_10f7_2c06_1d6a, - 0xf768_f6f3_811b_ea15, - 0xe424_fc9a_ab5a_512b, - 0x8cd5_8db9_9cab_5001, - 0x0883_e4bf_d946_bc32 - }))); + var b = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xf120_cb98_b16f_d84b, + 0x5fb5_10cf_f3de_1d61, + 0x0f21_a5d0_69d8_c251, + 0xaa1f_d62f_34f2_839a, + 0x5a13_3515_7f89_913f, + 0x14a3_fe32_9643_c247 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x3516_cb98_b16c_82f9, + 0x926d_10c2_e126_1d5f, + 0x1709_e01a_0cc2_5fba, + 0x96c8_c960_b825_3f14, + 0x4927_c234_207e_51a9, + 0x18ae_b158_d542_c44e + })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xbf0d_cb98_b169_82fc, + 0xa679_10b7_1d1a_1d5c, + 0xb7c1_47c2_b8fb_06ff, + 0x1efa_710d_47d2_e7ce, + 0xed20_a79c_7e27_653c, + 0x02b8_5294_dac1_dfba + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x9d52_cb98_b180_82e5, + 0x621d_1111_5176_1d6f, + 0xe798_8260_3b48_af43, + 0x0ad3_1637_a4f4_da37, + 0xaeac_737c_5ac1_cf2e, + 0x006e_7e73_5b48_b824 + })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xe148_cb98_b17d_2d93, + 0x94d5_1104_3ebe_1d6c, + 0xef80_bca9_de32_4cac, + 0xf77c_0969_2827_95b1, + 0x9dc1_009a_fbb6_8f97, + 0x0479_3199_9a47_ba2b + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x253e_cb98_b179_d841, + 0xc78d_10f7_2c06_1d6a, + 0xf768_f6f3_811b_ea15, + 0xe424_fc9a_ab5a_512b, + 0x8cd5_8db9_9cab_5001, + 0x0883_e4bf_d946_bc32 + }))); - var c = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x6934_cb98_b176_82ef, - 0xfa45_10ea_194e_1d67, - 0xff51_313d_2405_877e, - 0xd0cd_efcc_2e8d_0ca5, - 0x7bea_1ad8_3da0_106b, - 0x0c8e_97e6_1845_be39 - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0x4779_cb98_b18d_82d8, - 0xb5e9_1144_4daa_1d7a, - 0x2f28_6bda_a653_2fc2, - 0xbca6_94f6_8bae_ff0f, - 0x3d75_e6b8_1a3a_7a5d, - 0x0a44_c3c4_98cc_96a3 - })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x8b6f_cb98_b18a_2d86, - 0xe8a1_1137_3af2_1d77, - 0x3710_a624_493c_cd2b, - 0xa94f_8828_0ee1_ba89, - 0x2c8a_73d6_bb2f_3ac7, - 0x0e4f_76ea_d7cb_98aa - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0xcf65_cb98_b186_d834, - 0x1b59_112a_283a_1d74, - 0x3ef8_e06d_ec26_6a95, - 0x95f8_7b59_9214_7603, - 0x1b9f_00f5_5c23_fb31, - 0x125a_2a11_16ca_9ab1 - })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x135b_cb98_b183_82e2, - 0x4e11_111d_1582_1d72, - 0x46e1_1ab7_8f10_07fe, - 0x82a1_6e8b_1547_317d, - 0x0ab3_8e13_fd18_bb9b, - 0x1664_dd37_55c9_9cb8 - }), c1: Fp.FromRawUnchecked(new ulong[] - { - 0xce65_cb98_b131_8334, - 0xc759_0fdb_7c3a_1d2e, - 0x6fcb_8164_9d1c_8eb3, - 0x0d44_004d_1727_356a, - 0x3746_b738_a7d0_d296, - 0x136c_144a_96b1_34fc - }))); + var c = new Fp6(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x6934_cb98_b176_82ef, + 0xfa45_10ea_194e_1d67, + 0xff51_313d_2405_877e, + 0xd0cd_efcc_2e8d_0ca5, + 0x7bea_1ad8_3da0_106b, + 0x0c8e_97e6_1845_be39 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0x4779_cb98_b18d_82d8, + 0xb5e9_1144_4daa_1d7a, + 0x2f28_6bda_a653_2fc2, + 0xbca6_94f6_8bae_ff0f, + 0x3d75_e6b8_1a3a_7a5d, + 0x0a44_c3c4_98cc_96a3 + })), c1: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x8b6f_cb98_b18a_2d86, + 0xe8a1_1137_3af2_1d77, + 0x3710_a624_493c_cd2b, + 0xa94f_8828_0ee1_ba89, + 0x2c8a_73d6_bb2f_3ac7, + 0x0e4f_76ea_d7cb_98aa + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0xcf65_cb98_b186_d834, + 0x1b59_112a_283a_1d74, + 0x3ef8_e06d_ec26_6a95, + 0x95f8_7b59_9214_7603, + 0x1b9f_00f5_5c23_fb31, + 0x125a_2a11_16ca_9ab1 + })), c2: new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x135b_cb98_b183_82e2, + 0x4e11_111d_1582_1d72, + 0x46e1_1ab7_8f10_07fe, + 0x82a1_6e8b_1547_317d, + 0x0ab3_8e13_fd18_bb9b, + 0x1664_dd37_55c9_9cb8 + }), c1: Fp.FromRawUnchecked(new ulong[] + { + 0xce65_cb98_b131_8334, + 0xc759_0fdb_7c3a_1d2e, + 0x6fcb_8164_9d1c_8eb3, + 0x0d44_004d_1727_356a, + 0x3746_b738_a7d0_d296, + 0x136c_144a_96b1_34fc + }))); - Assert.AreEqual(a * a, a.Square()); - Assert.AreEqual(b * b, b.Square()); - Assert.AreEqual(c * c, c.Square()); + Assert.AreEqual(a * a, a.Square()); + Assert.AreEqual(b * b, b.Square()); + Assert.AreEqual(c * c, c.Square()); - Assert.AreEqual((a + b) * c.Square(), (c * c * a) + (c * c * b)); + Assert.AreEqual((a + b) * c.Square(), (c * c * a) + (c * c * b)); - Assert.AreEqual(a.Invert() * b.Invert(), (a * b).Invert()); - Assert.AreEqual(Fp6.One, a.Invert() * a); + Assert.AreEqual(a.Invert() * b.Invert(), (a * b).Invert()); + Assert.AreEqual(Fp6.One, a.Invert() * a); + } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs index ab1a598b3c..89a978153c 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G1.cs @@ -13,207 +13,33 @@ using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; using static Neo.Cryptography.BLS12_381.G1Constants; -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_G1 +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestBeta() - { - Assert.AreEqual(Fp.FromBytes(new byte[] - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, 0x76, - 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, 0x3b, - 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, - 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe - }), BETA); - Assert.AreNotEqual(Fp.One, BETA); - Assert.AreNotEqual(Fp.One, BETA * BETA); - Assert.AreEqual(Fp.One, BETA * BETA * BETA); - } - - [TestMethod] - public void TestIsOnCurve() - { - Assert.IsTrue(G1Affine.Identity.IsOnCurve); - Assert.IsTrue(G1Affine.Generator.IsOnCurve); - Assert.IsTrue(G1Projective.Identity.IsOnCurve); - Assert.IsTrue(G1Projective.Generator.IsOnCurve); - - Fp z = Fp.FromRawUnchecked(new ulong[] - { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }); - - var gen = G1Affine.Generator; - G1Projective test = new(gen.X * z, gen.Y * z, in z); - - Assert.IsTrue(test.IsOnCurve); - - test = new(in z, in test.Y, in test.Z); - Assert.IsFalse(test.IsOnCurve); - } - - [TestMethod] - public void TestAffinePointEquality() - { - var a = G1Affine.Generator; - var b = G1Affine.Identity; - - Assert.AreEqual(a, a); - Assert.AreEqual(b, b); - Assert.AreNotEqual(a, b); - } - - [TestMethod] - public void TestProjectivePointEquality() - { - var a = G1Projective.Generator; - var b = G1Projective.Identity; - - Assert.AreEqual(a, a); - Assert.AreEqual(b, b); - Assert.AreNotEqual(a, b); - - Fp z = Fp.FromRawUnchecked(new ulong[] - { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }); - - G1Projective c = new(a.X * z, a.Y * z, in z); - Assert.IsTrue(c.IsOnCurve); - - Assert.AreEqual(a, c); - Assert.AreNotEqual(b, c); - - c = new(in c.X, -c.Y, in c.Z); - Assert.IsTrue(c.IsOnCurve); - - Assert.AreNotEqual(a, c); - Assert.AreNotEqual(b, c); - - c = new(in z, -c.Y, in c.Z); - Assert.IsFalse(c.IsOnCurve); - Assert.AreNotEqual(a, b); - Assert.AreNotEqual(a, c); - Assert.AreNotEqual(b, c); - } - - [TestMethod] - public void TestConditionallySelectAffine() - { - var a = G1Affine.Generator; - var b = G1Affine.Identity; - - Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); - Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); - } - - [TestMethod] - public void TestConditionallySelectProjective() + [TestClass] + public class UT_G1 { - var a = G1Projective.Generator; - var b = G1Projective.Identity; - - Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); - Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); - } - - [TestMethod] - public void TestProjectiveToAffine() - { - var a = G1Projective.Generator; - var b = G1Projective.Identity; - - Assert.IsTrue(new G1Affine(a).IsOnCurve); - Assert.IsFalse(new G1Affine(a).IsIdentity); - Assert.IsTrue(new G1Affine(b).IsOnCurve); - Assert.IsTrue(new G1Affine(b).IsIdentity); - - Fp z = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestBeta() { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }); - - G1Projective c = new(a.X * z, a.Y * z, in z); - - Assert.AreEqual(G1Affine.Generator, new G1Affine(c)); - } - - [TestMethod] - public void TestAffineToProjective() - { - var a = G1Affine.Generator; - var b = G1Affine.Identity; - - Assert.IsTrue(new G1Projective(a).IsOnCurve); - Assert.IsFalse(new G1Projective(a).IsIdentity); - Assert.IsTrue(new G1Projective(b).IsOnCurve); - Assert.IsTrue(new G1Projective(b).IsIdentity); - } - - [TestMethod] - public void TestDoubling() - { - { - var tmp = G1Projective.Identity.Double(); - Assert.IsTrue(tmp.IsIdentity); - Assert.IsTrue(tmp.IsOnCurve); - } - { - var tmp = G1Projective.Generator.Double(); - Assert.IsFalse(tmp.IsIdentity); - Assert.IsTrue(tmp.IsOnCurve); - - Assert.AreEqual(new G1Affine(Fp.FromRawUnchecked(new ulong[] - { - 0x53e9_78ce_58a9_ba3c, - 0x3ea0_583c_4f3d_65f9, - 0x4d20_bb47_f001_2960, - 0xa54c_664a_e5b2_b5d9, - 0x26b5_52a3_9d7e_b21f, - 0x0008_895d_26e6_8785 - }), Fp.FromRawUnchecked(new ulong[] + Assert.AreEqual(Fp.FromBytes(new byte[] { - 0x7011_0b32_9829_3940, - 0xda33_c539_3f1f_6afc, - 0xb86e_dfd1_6a5a_a785, - 0xaec6_d1c9_e7b1_c895, - 0x25cf_c2b5_22d1_1720, - 0x0636_1c83_f8d0_9b15 - })), new G1Affine(tmp)); + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, 0xdf, 0x76, + 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, 0xdd, 0xb3, 0xa9, 0x3b, + 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe + }), BETA); + Assert.AreNotEqual(Fp.One, BETA); + Assert.AreNotEqual(Fp.One, BETA * BETA); + Assert.AreEqual(Fp.One, BETA * BETA * BETA); } - } - [TestMethod] - public void TestProjectiveAddition() - { + [TestMethod] + public void TestIsOnCurve() { - var a = G1Projective.Identity; - var b = G1Projective.Identity; - var c = a + b; - Assert.IsTrue(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - } - { - var a = G1Projective.Identity; - var b = G1Projective.Generator; + Assert.IsTrue(G1Affine.Identity.IsOnCurve); + Assert.IsTrue(G1Affine.Generator.IsOnCurve); + Assert.IsTrue(G1Projective.Identity.IsOnCurve); + Assert.IsTrue(G1Projective.Generator.IsOnCurve); Fp z = Fp.FromRawUnchecked(new ulong[] { @@ -225,15 +51,35 @@ public void TestProjectiveAddition() 0x12b1_08ac_3364_3c3e }); - b = new(b.X * z, b.Y * z, in z); - var c = a + b; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G1Projective.Generator, c); + var gen = G1Affine.Generator; + G1Projective test = new(gen.X * z, gen.Y * z, in z); + + Assert.IsTrue(test.IsOnCurve); + + test = new(in z, in test.Y, in test.Z); + Assert.IsFalse(test.IsOnCurve); } + + [TestMethod] + public void TestAffinePointEquality() + { + var a = G1Affine.Generator; + var b = G1Affine.Identity; + + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); + } + + [TestMethod] + public void TestProjectivePointEquality() { - var a = G1Projective.Identity; - var b = G1Projective.Generator; + var a = G1Projective.Generator; + var b = G1Projective.Identity; + + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); Fp z = Fp.FromRawUnchecked(new ulong[] { @@ -245,80 +91,55 @@ public void TestProjectiveAddition() 0x12b1_08ac_3364_3c3e }); - b = new(b.X * z, b.Y * z, in z); - var c = b + a; - Assert.IsFalse(c.IsIdentity); + G1Projective c = new(a.X * z, a.Y * z, in z); Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G1Projective.Generator, c); - } - { - var a = G1Projective.Generator.Double().Double(); // 4P - var b = G1Projective.Generator.Double(); // 2P - var c = a + b; - var d = G1Projective.Generator; - for (int i = 0; i < 5; i++) - { - d += G1Projective.Generator; - } - Assert.IsFalse(c.IsIdentity); + Assert.AreEqual(a, c); + Assert.AreNotEqual(b, c); + + c = new(in c.X, -c.Y, in c.Z); Assert.IsTrue(c.IsOnCurve); - Assert.IsFalse(d.IsIdentity); - Assert.IsTrue(d.IsOnCurve); - Assert.AreEqual(c, d); + + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); + + c = new(in z, -c.Y, in c.Z); + Assert.IsFalse(c.IsOnCurve); + Assert.AreNotEqual(a, b); + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); } + + [TestMethod] + public void TestConditionallySelectAffine() { - Fp beta = Fp.FromRawUnchecked(new ulong[] - { - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741 - }); - beta = beta.Square(); - var a = G1Projective.Generator.Double().Double(); - var b = new G1Projective(a.X * beta, -a.Y, in a.Z); - Assert.IsTrue(a.IsOnCurve); - Assert.IsTrue(b.IsOnCurve); - - var c = a + b; - Assert.AreEqual(new G1Affine(new G1Projective(Fp.FromRawUnchecked(new ulong[] - { - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - }), in Fp.One)), new G1Affine(c)); - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); + var a = G1Affine.Generator; + var b = G1Affine.Identity; + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); } - } - [TestMethod] - public void TestMixedAddition() - { + [TestMethod] + public void TestConditionallySelectProjective() { - var a = G1Affine.Identity; + var a = G1Projective.Generator; var b = G1Projective.Identity; - var c = a + b; - Assert.IsTrue(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); } + + [TestMethod] + public void TestProjectiveToAffine() { - var a = G1Affine.Identity; - var b = G1Projective.Generator; + var a = G1Projective.Generator; + var b = G1Projective.Identity; + + Assert.IsTrue(new G1Affine(a).IsOnCurve); + Assert.IsFalse(new G1Affine(a).IsIdentity); + Assert.IsTrue(new G1Affine(b).IsOnCurve); + Assert.IsTrue(new G1Affine(b).IsIdentity); Fp z = Fp.FromRawUnchecked(new ulong[] { @@ -330,272 +151,452 @@ public void TestMixedAddition() 0x12b1_08ac_3364_3c3e }); - b = new(b.X * z, b.Y * z, in z); - var c = a + b; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G1Projective.Generator, c); + G1Projective c = new(a.X * z, a.Y * z, in z); + + Assert.AreEqual(G1Affine.Generator, new G1Affine(c)); } + + [TestMethod] + public void TestAffineToProjective() { - var a = G1Affine.Identity; - var b = G1Projective.Generator; + var a = G1Affine.Generator; + var b = G1Affine.Identity; - Fp z = Fp.FromRawUnchecked(new ulong[] + Assert.IsTrue(new G1Projective(a).IsOnCurve); + Assert.IsFalse(new G1Projective(a).IsIdentity); + Assert.IsTrue(new G1Projective(b).IsOnCurve); + Assert.IsTrue(new G1Projective(b).IsIdentity); + } + + [TestMethod] + public void TestDoubling() + { { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }); + var tmp = G1Projective.Identity.Double(); + Assert.IsTrue(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + } + { + var tmp = G1Projective.Generator.Double(); + Assert.IsFalse(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); - b = new(b.X * z, b.Y * z, in z); - var c = b + a; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G1Projective.Generator, c); + Assert.AreEqual(new G1Affine(Fp.FromRawUnchecked(new ulong[] + { + 0x53e9_78ce_58a9_ba3c, + 0x3ea0_583c_4f3d_65f9, + 0x4d20_bb47_f001_2960, + 0xa54c_664a_e5b2_b5d9, + 0x26b5_52a3_9d7e_b21f, + 0x0008_895d_26e6_8785 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7011_0b32_9829_3940, + 0xda33_c539_3f1f_6afc, + 0xb86e_dfd1_6a5a_a785, + 0xaec6_d1c9_e7b1_c895, + 0x25cf_c2b5_22d1_1720, + 0x0636_1c83_f8d0_9b15 + })), new G1Affine(tmp)); + } } + + [TestMethod] + public void TestProjectiveAddition() { - var a = G1Projective.Generator.Double().Double(); // 4P - var b = G1Projective.Generator.Double(); // 2P - var c = a + b; + { + var a = G1Projective.Identity; + var b = G1Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + { + var a = G1Projective.Identity; + var b = G1Projective.Generator; - var d = G1Projective.Generator; - for (int i = 0; i < 5; i++) + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } { - d += G1Affine.Generator; + var a = G1Projective.Identity; + var b = G1Projective.Generator; + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } + { + var a = G1Projective.Generator.Double().Double(); // 4P + var b = G1Projective.Generator.Double(); // 2P + var c = a + b; + + var d = G1Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G1Projective.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + { + Fp beta = Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }); + beta = beta.Square(); + var a = G1Projective.Generator.Double().Double(); + var b = new G1Projective(a.X * beta, -a.Y, in a.Z); + Assert.IsTrue(a.IsOnCurve); + Assert.IsTrue(b.IsOnCurve); + + var c = a + b; + Assert.AreEqual(new G1Affine(new G1Projective(Fp.FromRawUnchecked(new ulong[] + { + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44, + }), in Fp.One)), new G1Affine(c)); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); } - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.IsFalse(d.IsIdentity); - Assert.IsTrue(d.IsOnCurve); - Assert.AreEqual(c, d); } + + [TestMethod] + public void TestMixedAddition() { - Fp beta = Fp.FromRawUnchecked(new ulong[] { - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741 - }); - beta = beta.Square(); - var a = G1Projective.Generator.Double().Double(); - var b = new G1Projective(a.X * beta, -a.Y, in a.Z); - var a2 = new G1Affine(a); - Assert.IsTrue(a2.IsOnCurve); - Assert.IsTrue(b.IsOnCurve); - - var c = a2 + b; - Assert.AreEqual(new G1Affine(new G1Projective(Fp.FromRawUnchecked(new ulong[] + var a = G1Affine.Identity; + var b = G1Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } { - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6 - }), Fp.FromRawUnchecked(new ulong[] + var a = G1Affine.Identity; + var b = G1Projective.Generator; + + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } { - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44 - }), Fp.One)), new G1Affine(c)); - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - } - } + var a = G1Affine.Identity; + var b = G1Projective.Generator; - [TestMethod] - public void TestProjectiveNegationAndSubtraction() - { - var a = G1Projective.Generator.Double(); - Assert.AreEqual(a + (-a), G1Projective.Identity); - Assert.AreEqual(a + (-a), a - a); - } + Fp z = Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }); + + b = new(b.X * z, b.Y * z, in z); + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G1Projective.Generator, c); + } + { + var a = G1Projective.Generator.Double().Double(); // 4P + var b = G1Projective.Generator.Double(); // 2P + var c = a + b; - [TestMethod] - public void TestAffineNegationAndSubtraction() - { - var a = G1Affine.Generator; - Assert.AreEqual(G1Projective.Identity, new G1Projective(a) + (-a)); - Assert.AreEqual(new G1Projective(a) + (-a), new G1Projective(a) - a); - } + var d = G1Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G1Affine.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + { + Fp beta = Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }); + beta = beta.Square(); + var a = G1Projective.Generator.Double().Double(); + var b = new G1Projective(a.X * beta, -a.Y, in a.Z); + var a2 = new G1Affine(a); + Assert.IsTrue(a2.IsOnCurve); + Assert.IsTrue(b.IsOnCurve); + + var c = a2 + b; + Assert.AreEqual(new G1Affine(new G1Projective(Fp.FromRawUnchecked(new ulong[] + { + 0x29e1_e987_ef68_f2d0, + 0xc5f3_ec53_1db0_3233, + 0xacd6_c4b6_ca19_730f, + 0x18ad_9e82_7bc2_bab7, + 0x46e3_b2c5_785c_c7a9, + 0x07e5_71d4_2d22_ddd6 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x94d1_17a7_e5a5_39e7, + 0x8e17_ef67_3d4b_5d22, + 0x9d74_6aaf_508a_33ea, + 0x8c6d_883d_2516_c9a2, + 0x0bc3_b8d5_fb04_47f7, + 0x07bf_a4c7_210f_4f44 + }), Fp.One)), new G1Affine(c)); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + } - [TestMethod] - public void TestProjectiveScalarMultiplication() - { - var g = G1Projective.Generator; - var a = Scalar.FromRaw(new ulong[] - { - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2 - }); - var b = Scalar.FromRaw(new ulong[] + [TestMethod] + public void TestProjectiveNegationAndSubtraction() { - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20 - }); - var c = a * b; - - Assert.AreEqual(g * a * b, g * c); - } + var a = G1Projective.Generator.Double(); + Assert.AreEqual(a + (-a), G1Projective.Identity); + Assert.AreEqual(a + (-a), a - a); + } - [TestMethod] - public void TestAffineScalarMultiplication() - { - var g = G1Affine.Generator; - var a = Scalar.FromRaw(new ulong[] + [TestMethod] + public void TestAffineNegationAndSubtraction() { - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2 - }); - var b = Scalar.FromRaw(new ulong[] - { - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20 - }); - var c = a * b; - - Assert.AreEqual(new G1Affine(g * a) * b, g * c); - } + var a = G1Affine.Generator; + Assert.AreEqual(G1Projective.Identity, new G1Projective(a) + (-a)); + Assert.AreEqual(new G1Projective(a) + (-a), new G1Projective(a) - a); + } - [TestMethod] - public void TestIsTorsionFree() - { - var a = new G1Affine(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestProjectiveScalarMultiplication() { - 0x0aba_f895_b97e_43c8, - 0xba4c_6432_eb9b_61b0, - 0x1250_6f52_adfe_307f, - 0x7502_8c34_3933_6b72, - 0x8474_4f05_b8e9_bd71, - 0x113d_554f_b095_54f7 - }), Fp.FromRawUnchecked(new ulong[] + var g = G1Projective.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(g * a * b, g * c); + } + + [TestMethod] + public void TestAffineScalarMultiplication() { - 0x73e9_0e88_f5cf_01c0, - 0x3700_7b65_dd31_97e2, - 0x5cf9_a199_2f0d_7c78, - 0x4f83_c10b_9eb3_330d, - 0xf6a6_3f6f_07f6_0961, - 0x0c53_b5b9_7e63_4df3 - })); - Assert.IsFalse(a.IsTorsionFree); - - Assert.IsTrue(G1Affine.Identity.IsTorsionFree); - Assert.IsTrue(G1Affine.Generator.IsTorsionFree); - } + var g = G1Affine.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; - [TestMethod] - public void TestMulByX() - { - // multiplying by `x` a point in G1 is the same as multiplying by - // the equivalent scalar. - var generator = G1Projective.Generator; - var x = BLS_X_IS_NEGATIVE ? -new Scalar(BLS_X) : new Scalar(BLS_X); - Assert.AreEqual(generator.MulByX(), generator * x); - - var point = G1Projective.Generator * new Scalar(42); - Assert.AreEqual(point.MulByX(), point * x); - } + Assert.AreEqual(new G1Affine(g * a) * b, g * c); + } - [TestMethod] - public void TestClearCofactor() - { - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - var generator = G1Projective.Generator; - Assert.IsTrue(generator.ClearCofactor().IsOnCurve); - var id = G1Projective.Identity; - Assert.IsTrue(id.ClearCofactor().IsOnCurve); - - var z = Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestIsTorsionFree() { - 0x3d2d1c670671394e, - 0x0ee3a800a2f7c1ca, - 0x270f4f21da2e5050, - 0xe02840a53f1be768, - 0x55debeb597512690, - 0x08bd25353dc8f791 - }); - - var point = new G1Projective(Fp.FromRawUnchecked(new ulong[] + var a = new G1Affine(Fp.FromRawUnchecked(new ulong[] + { + 0x0aba_f895_b97e_43c8, + 0xba4c_6432_eb9b_61b0, + 0x1250_6f52_adfe_307f, + 0x7502_8c34_3933_6b72, + 0x8474_4f05_b8e9_bd71, + 0x113d_554f_b095_54f7 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x73e9_0e88_f5cf_01c0, + 0x3700_7b65_dd31_97e2, + 0x5cf9_a199_2f0d_7c78, + 0x4f83_c10b_9eb3_330d, + 0xf6a6_3f6f_07f6_0961, + 0x0c53_b5b9_7e63_4df3 + })); + Assert.IsFalse(a.IsTorsionFree); + + Assert.IsTrue(G1Affine.Identity.IsTorsionFree); + Assert.IsTrue(G1Affine.Generator.IsTorsionFree); + } + + [TestMethod] + public void TestMulByX() { - 0x48af5ff540c817f0, - 0xd73893acaf379d5a, - 0xe6c43584e18e023c, - 0x1eda39c30f188b3e, - 0xf618c6d3ccc0f8d8, - 0x0073542cd671e16c - }) * z, Fp.FromRawUnchecked(new ulong[] + // multiplying by `x` a point in G1 is the same as multiplying by + // the equivalent scalar. + var generator = G1Projective.Generator; + var x = BLS_X_IS_NEGATIVE ? -new Scalar(BLS_X) : new Scalar(BLS_X); + Assert.AreEqual(generator.MulByX(), generator * x); + + var point = G1Projective.Generator * new Scalar(42); + Assert.AreEqual(point.MulByX(), point * x); + } + + [TestMethod] + public void TestClearCofactor() { - 0x57bf8be79461d0ba, - 0xfc61459cee3547c3, - 0x0d23567df1ef147b, - 0x0ee187bcce1d9b64, - 0xb0c8cfbe9dc8fdc1, - 0x1328661767ef368b - }), z.Square() * z); - - Assert.IsTrue(point.IsOnCurve); - Assert.IsFalse(new G1Affine(point).IsTorsionFree); - var cleared_point = point.ClearCofactor(); - Assert.IsTrue(cleared_point.IsOnCurve); - Assert.IsTrue(new G1Affine(cleared_point).IsTorsionFree); - - // in BLS12-381 the cofactor in G1 can be - // cleared multiplying by (1-x) - var h_eff = new Scalar(1) + new Scalar(BLS_X); - Assert.AreEqual(point.ClearCofactor(), point * h_eff); - } + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + var generator = G1Projective.Generator; + Assert.IsTrue(generator.ClearCofactor().IsOnCurve); + var id = G1Projective.Identity; + Assert.IsTrue(id.ClearCofactor().IsOnCurve); + + var z = Fp.FromRawUnchecked(new ulong[] + { + 0x3d2d1c670671394e, + 0x0ee3a800a2f7c1ca, + 0x270f4f21da2e5050, + 0xe02840a53f1be768, + 0x55debeb597512690, + 0x08bd25353dc8f791 + }); - [TestMethod] - public void TestBatchNormalize() - { - var a = G1Projective.Generator.Double(); - var b = a.Double(); - var c = b.Double(); + var point = new G1Projective(Fp.FromRawUnchecked(new ulong[] + { + 0x48af5ff540c817f0, + 0xd73893acaf379d5a, + 0xe6c43584e18e023c, + 0x1eda39c30f188b3e, + 0xf618c6d3ccc0f8d8, + 0x0073542cd671e16c + }) * z, Fp.FromRawUnchecked(new ulong[] + { + 0x57bf8be79461d0ba, + 0xfc61459cee3547c3, + 0x0d23567df1ef147b, + 0x0ee187bcce1d9b64, + 0xb0c8cfbe9dc8fdc1, + 0x1328661767ef368b + }), z.Square() * z); + + Assert.IsTrue(point.IsOnCurve); + Assert.IsFalse(new G1Affine(point).IsTorsionFree); + var cleared_point = point.ClearCofactor(); + Assert.IsTrue(cleared_point.IsOnCurve); + Assert.IsTrue(new G1Affine(cleared_point).IsTorsionFree); + + // in BLS12-381 the cofactor in G1 can be + // cleared multiplying by (1-x) + var h_eff = new Scalar(1) + new Scalar(BLS_X); + Assert.AreEqual(point.ClearCofactor(), point * h_eff); + } - foreach (bool a_identity in new[] { false, true }) + [TestMethod] + public void TestBatchNormalize() { - foreach (bool b_identity in new[] { false, true }) + var a = G1Projective.Generator.Double(); + var b = a.Double(); + var c = b.Double(); + + foreach (bool a_identity in new[] { false, true }) { - foreach (bool c_identity in new[] { false, true }) + foreach (bool b_identity in new[] { false, true }) { - var v = new[] { a, b, c }; - if (a_identity) - { - v[0] = G1Projective.Identity; - } - if (b_identity) - { - v[1] = G1Projective.Identity; - } - if (c_identity) + foreach (bool c_identity in new[] { false, true }) { - v[2] = G1Projective.Identity; + var v = new[] { a, b, c }; + if (a_identity) + { + v[0] = G1Projective.Identity; + } + if (b_identity) + { + v[1] = G1Projective.Identity; + } + if (c_identity) + { + v[2] = G1Projective.Identity; + } + + var t = new G1Affine[3]; + var expected = new[] { new G1Affine(v[0]), new G1Affine(v[1]), new G1Affine(v[2]) }; + + G1Projective.BatchNormalize(v, t); + + CollectionAssert.AreEqual(expected, t); } - - var t = new G1Affine[3]; - var expected = new[] { new G1Affine(v[0]), new G1Affine(v[1]), new G1Affine(v[2]) }; - - G1Projective.BatchNormalize(v, t); - - CollectionAssert.AreEqual(expected, t); } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs index 7b8cadb90e..ee36f5b5f0 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_G2.cs @@ -12,810 +12,811 @@ using static Neo.Cryptography.BLS12_381.Constants; using static Neo.Cryptography.BLS12_381.ConstantTimeUtility; -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_G2 +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestIsOnCurve() + [TestClass] + public class UT_G2 { - Assert.IsTrue(G2Affine.Identity.IsOnCurve); - Assert.IsTrue(G2Affine.Generator.IsOnCurve); - Assert.IsTrue(G2Projective.Identity.IsOnCurve); - Assert.IsTrue(G2Projective.Generator.IsOnCurve); - - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestIsOnCurve() { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }), Fp.FromRawUnchecked(new ulong[] - { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - var gen = G2Affine.Generator; - var test = new G2Projective(gen.X * z, gen.Y * z, z); + Assert.IsTrue(G2Affine.Identity.IsOnCurve); + Assert.IsTrue(G2Affine.Generator.IsOnCurve); + Assert.IsTrue(G2Projective.Identity.IsOnCurve); + Assert.IsTrue(G2Projective.Generator.IsOnCurve); - Assert.IsTrue(test.IsOnCurve); + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); - test = new(in z, in test.Y, in test.Z); - Assert.IsFalse(test.IsOnCurve); - } + var gen = G2Affine.Generator; + var test = new G2Projective(gen.X * z, gen.Y * z, z); - [TestMethod] - public void TestAffinePointEquality() - { - var a = G2Affine.Generator; - var b = G2Affine.Identity; + Assert.IsTrue(test.IsOnCurve); - Assert.AreEqual(a, a); - Assert.AreEqual(b, b); - Assert.AreNotEqual(a, b); - } + test = new(in z, in test.Y, in test.Z); + Assert.IsFalse(test.IsOnCurve); + } - [TestMethod] - public void TestProjectivePointEquality() - { - var a = G2Projective.Generator; - var b = G2Projective.Identity; + [TestMethod] + public void TestAffinePointEquality() + { + var a = G2Affine.Generator; + var b = G2Affine.Identity; - Assert.AreEqual(a, a); - Assert.AreEqual(b, b); - Assert.AreNotEqual(a, b); + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); + } - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestProjectivePointEquality() { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }), Fp.FromRawUnchecked(new ulong[] - { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - var c = new G2Projective(a.X * z, a.Y * z, in z); - Assert.IsTrue(c.IsOnCurve); - - Assert.AreEqual(a, c); - Assert.AreNotEqual(b, c); - - c = new(in c.X, -c.Y, in c.Z); - Assert.IsTrue(c.IsOnCurve); - - Assert.AreNotEqual(a, c); - Assert.AreNotEqual(b, c); - - c = new(in z, -c.Y, in c.Z); - Assert.IsFalse(c.IsOnCurve); - Assert.AreNotEqual(a, b); - Assert.AreNotEqual(a, c); - Assert.AreNotEqual(b, c); - } + var a = G2Projective.Generator; + var b = G2Projective.Identity; - [TestMethod] - public void TestConditionallySelectAffine() - { - var a = G2Affine.Generator; - var b = G2Affine.Identity; + Assert.AreEqual(a, a); + Assert.AreEqual(b, b); + Assert.AreNotEqual(a, b); - Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); - Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); - } + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + var c = new G2Projective(a.X * z, a.Y * z, in z); + Assert.IsTrue(c.IsOnCurve); - [TestMethod] - public void TestConditionallySelectProjective() - { - var a = G2Projective.Generator; - var b = G2Projective.Identity; + Assert.AreEqual(a, c); + Assert.AreNotEqual(b, c); - Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); - Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); - } + c = new(in c.X, -c.Y, in c.Z); + Assert.IsTrue(c.IsOnCurve); - [TestMethod] - public void TestProjectiveToAffine() - { - var a = G2Projective.Generator; - var b = G2Projective.Identity; + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); - Assert.IsTrue(new G2Affine(a).IsOnCurve); - Assert.IsFalse(new G2Affine(a).IsIdentity); - Assert.IsTrue(new G2Affine(b).IsOnCurve); - Assert.IsTrue(new G2Affine(b).IsIdentity); + c = new(in z, -c.Y, in c.Z); + Assert.IsFalse(c.IsOnCurve); + Assert.AreNotEqual(a, b); + Assert.AreNotEqual(a, c); + Assert.AreNotEqual(b, c); + } - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestConditionallySelectAffine() { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - var c = new G2Projective(a.X * z, a.Y * z, in z); - - Assert.AreEqual(G2Affine.Generator, new G2Affine(c)); - } - - [TestMethod] - public void TestAffineToProjective() - { - var a = G2Affine.Generator; - var b = G2Affine.Identity; + var a = G2Affine.Generator; + var b = G2Affine.Identity; - Assert.IsTrue(new G2Projective(a).IsOnCurve); - Assert.IsFalse(new G2Projective(a).IsIdentity); - Assert.IsTrue(new G2Projective(b).IsOnCurve); - Assert.IsTrue(new G2Projective(b).IsIdentity); - } + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); + } - [TestMethod] - public void TestDoubling() - { + [TestMethod] + public void TestConditionallySelectProjective() { - var tmp = G2Projective.Identity.Double(); - Assert.IsTrue(tmp.IsIdentity); - Assert.IsTrue(tmp.IsOnCurve); + var a = G2Projective.Generator; + var b = G2Projective.Identity; + + Assert.AreEqual(a, ConditionalSelect(in a, in b, false)); + Assert.AreEqual(b, ConditionalSelect(in a, in b, true)); } + + [TestMethod] + public void TestProjectiveToAffine() { - var tmp = G2Projective.Generator.Double(); - Assert.IsFalse(tmp.IsIdentity); - Assert.IsTrue(tmp.IsOnCurve); - - Assert.AreEqual(new G2Affine(tmp), new G2Affine(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xe9d9_e2da_9620_f98b, - 0x54f1_1993_46b9_7f36, - 0x3db3_b820_376b_ed27, - 0xcfdb_31c9_b0b6_4f4c, - 0x41d7_c127_8635_4493, - 0x0571_0794_c255_c064 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xd6c1_d3ca_6ea0_d06e, - 0xda0c_bd90_5595_489f, - 0x4f53_52d4_3479_221d, - 0x8ade_5d73_6f8c_97e0, - 0x48cc_8433_925e_f70e, - 0x08d7_ea71_ea91_ef81 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] + var a = G2Projective.Generator; + var b = G2Projective.Identity; + + Assert.IsTrue(new G2Affine(a).IsOnCurve); + Assert.IsFalse(new G2Affine(a).IsIdentity); + Assert.IsTrue(new G2Affine(b).IsOnCurve); + Assert.IsTrue(new G2Affine(b).IsIdentity); + + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] { - 0x15ba_26eb_4b0d_186f, - 0x0d08_6d64_b7e9_e01e, - 0xc8b8_48dd_652f_4c78, - 0xeecf_46a6_123b_ae4f, - 0x255e_8dd8_b6dc_812a, - 0x1641_42af_21dc_f93f + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e }), Fp.FromRawUnchecked(new ulong[] { - 0xf9b4_a1a8_9598_4db4, - 0xd417_b114_cccf_f748, - 0x6856_301f_c89f_086e, - 0x41c7_7787_8931_e3da, - 0x3556_b155_066a_2105, - 0x00ac_f7d3_25cb_89cf - })))); + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + var c = new G2Projective(a.X * z, a.Y * z, in z); + + Assert.AreEqual(G2Affine.Generator, new G2Affine(c)); } - } - [TestMethod] - public void TestProjectiveAddition() - { + [TestMethod] + public void TestAffineToProjective() { - var a = G2Projective.Identity; - var b = G2Projective.Identity; - var c = a + b; - Assert.IsTrue(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); + var a = G2Affine.Generator; + var b = G2Affine.Identity; + + Assert.IsTrue(new G2Projective(a).IsOnCurve); + Assert.IsFalse(new G2Projective(a).IsIdentity); + Assert.IsTrue(new G2Projective(b).IsOnCurve); + Assert.IsTrue(new G2Projective(b).IsIdentity); } + + [TestMethod] + public void TestDoubling() { - var a = G2Projective.Identity; - var b = G2Projective.Generator; { - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + var tmp = G2Projective.Identity.Double(); + Assert.IsTrue(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + } + { + var tmp = G2Projective.Generator.Double(); + Assert.IsFalse(tmp.IsIdentity); + Assert.IsTrue(tmp.IsOnCurve); + + Assert.AreEqual(new G2Affine(tmp), new G2Affine(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xe9d9_e2da_9620_f98b, + 0x54f1_1993_46b9_7f36, + 0x3db3_b820_376b_ed27, + 0xcfdb_31c9_b0b6_4f4c, + 0x41d7_c127_8635_4493, + 0x0571_0794_c255_c064 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xd6c1_d3ca_6ea0_d06e, + 0xda0c_bd90_5595_489f, + 0x4f53_52d4_3479_221d, + 0x8ade_5d73_6f8c_97e0, + 0x48cc_8433_925e_f70e, + 0x08d7_ea71_ea91_ef81 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e + 0x15ba_26eb_4b0d_186f, + 0x0d08_6d64_b7e9_e01e, + 0xc8b8_48dd_652f_4c78, + 0xeecf_46a6_123b_ae4f, + 0x255e_8dd8_b6dc_812a, + 0x1641_42af_21dc_f93f }), Fp.FromRawUnchecked(new ulong[] { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - b = new G2Projective(b.X * z, b.Y * z, in z); + 0xf9b4_a1a8_9598_4db4, + 0xd417_b114_cccf_f748, + 0x6856_301f_c89f_086e, + 0x41c7_7787_8931_e3da, + 0x3556_b155_066a_2105, + 0x00ac_f7d3_25cb_89cf + })))); } - var c = a + b; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G2Projective.Generator, c); } + + [TestMethod] + public void TestProjectiveAddition() { - var a = G2Projective.Identity; - var b = G2Projective.Generator; { - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + var a = G2Projective.Identity; + var b = G2Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } + { + var a = G2Projective.Identity; + var b = G2Projective.Generator; { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e - }), Fp.FromRawUnchecked(new ulong[] + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Projective.Identity; + var b = G2Projective.Generator; { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - b = new G2Projective(b.X * z, b.Y * z, in z); + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Projective.Generator.Double().Double(); // 4P + var b = G2Projective.Generator.Double(); // 2P + var c = a + b; + + var d = G2Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G2Projective.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); } - var c = b + a; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G2Projective.Generator, c); - } - { - var a = G2Projective.Generator.Double().Double(); // 4P - var b = G2Projective.Generator.Double(); // 2P - var c = a + b; - var d = G2Projective.Generator; - for (int i = 0; i < 5; i++) + // Degenerate case { - d += G2Projective.Generator; + var beta = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }), Fp.Zero); + beta = beta.Square(); + var a = G2Projective.Generator.Double().Double(); + var b = new G2Projective(a.X * beta, -a.Y, in a.Z); + Assert.IsTrue(a.IsOnCurve); + Assert.IsTrue(b.IsOnCurve); + + var c = a + b; + Assert.AreEqual( + new G2Affine(c), + new G2Affine(new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe + })), Fp2.One))); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); } - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.IsFalse(d.IsIdentity); - Assert.IsTrue(d.IsOnCurve); - Assert.AreEqual(c, d); } - // Degenerate case + [TestMethod] + public void TestMixedAddition() { - var beta = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741 - }), Fp.Zero); - beta = beta.Square(); - var a = G2Projective.Generator.Double().Double(); - var b = new G2Projective(a.X * beta, -a.Y, in a.Z); - Assert.IsTrue(a.IsOnCurve); - Assert.IsTrue(b.IsOnCurve); - - var c = a + b; - Assert.AreEqual( - new G2Affine(c), - new G2Affine(new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2 - }), Fp.FromRawUnchecked(new ulong[] { - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952 - })), new Fp2(Fp.FromRawUnchecked(new ulong[] + var a = G2Affine.Identity; + var b = G2Projective.Identity; + var c = a + b; + Assert.IsTrue(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + } { - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3 - }), Fp.FromRawUnchecked(new ulong[] + var a = G2Affine.Identity; + var b = G2Projective.Generator; + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = a + b; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } { - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe - })), Fp2.One))); - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - } - } + var a = G2Affine.Identity; + var b = G2Projective.Generator; + { + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xba7a_fa1f_9a6f_e250, + 0xfa0f_5b59_5eaf_e731, + 0x3bdc_4776_94c3_06e7, + 0x2149_be4b_3949_fa24, + 0x64aa_6e06_49b2_078c, + 0x12b1_08ac_3364_3c3e + }), Fp.FromRawUnchecked(new ulong[] + { + 0x1253_25df_3d35_b5a8, + 0xdc46_9ef5_555d_7fe3, + 0x02d7_16d2_4431_06a9, + 0x05a1_db59_a6ff_37d0, + 0x7cf7_784e_5300_bb8f, + 0x16a8_8922_c7a5_e844 + })); + + b = new G2Projective(b.X * z, b.Y * z, in z); + } + var c = b + a; + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.AreEqual(G2Projective.Generator, c); + } + { + var a = G2Projective.Generator.Double().Double(); // 4P + var b = G2Projective.Generator.Double(); // 2P + var c = a + b; - [TestMethod] - public void TestMixedAddition() - { - { - var a = G2Affine.Identity; - var b = G2Projective.Identity; - var c = a + b; - Assert.IsTrue(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - } - { - var a = G2Affine.Identity; - var b = G2Projective.Generator; + var d = G2Projective.Generator; + for (int i = 0; i < 5; i++) + { + d += G2Affine.Generator; + } + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); + Assert.IsFalse(d.IsIdentity); + Assert.IsTrue(d.IsOnCurve); + Assert.AreEqual(c, d); + } + + // Degenerate case { - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + var beta = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xcd03_c9e4_8671_f071, + 0x5dab_2246_1fcd_a5d2, + 0x5870_42af_d385_1b95, + 0x8eb6_0ebe_01ba_cb9e, + 0x03f9_7d6e_83d0_50d2, + 0x18f0_2065_5463_8741 + }), Fp.Zero); + beta = beta.Square(); + var _a = G2Projective.Generator.Double().Double(); + var b = new G2Projective(_a.X * beta, -_a.Y, in _a.Z); + var a = new G2Affine(_a); + Assert.IsTrue((a.IsOnCurve)); + Assert.IsTrue((b.IsOnCurve)); + + var c = a + b; + Assert.AreEqual(new G2Affine(new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e + 0x705a_bc79_9ca7_73d3, + 0xfe13_2292_c1d4_bf08, + 0xf37e_ce3e_07b2_b466, + 0x887e_1c43_f447_e301, + 0x1e09_70d0_33bc_77e8, + 0x1985_c81e_20a6_93f2 }), Fp.FromRawUnchecked(new ulong[] { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - b = new G2Projective(b.X * z, b.Y * z, in z); - } - var c = a + b; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G2Projective.Generator, c); - } - { - var a = G2Affine.Identity; - var b = G2Projective.Generator; - { - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + 0x1d79_b25d_b36a_b924, + 0x2394_8e4d_5296_39d3, + 0x471b_a7fb_0d00_6297, + 0x2c36_d4b4_465d_c4c0, + 0x82bb_c3cf_ec67_f538, + 0x051d_2728_b67b_f952 + })), new Fp2(Fp.FromRawUnchecked(new ulong[] { - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e + 0x41b1_bbf6_576c_0abf, + 0xb6cc_9371_3f7a_0f9a, + 0x6b65_b43e_48f3_f01f, + 0xfb7a_4cfc_af81_be4f, + 0x3e32_dadc_6ec2_2cb6, + 0x0bb0_fc49_d798_07e3 }), Fp.FromRawUnchecked(new ulong[] { - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844 - })); - - b = new G2Projective(b.X * z, b.Y * z, in z); + 0x7d13_9778_8f5f_2ddf, + 0xab29_0714_4ff0_d8e8, + 0x5b75_73e0_cdb9_1f92, + 0x4cb8_932d_d31d_af28, + 0x62bb_fac6_db05_2a54, + 0x11f9_5c16_d14c_3bbe + })), Fp2.One)), new G2Affine(c)); + Assert.IsFalse(c.IsIdentity); + Assert.IsTrue(c.IsOnCurve); } - var c = b + a; - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.AreEqual(G2Projective.Generator, c); } + + [TestMethod] + public void TestProjectiveNegationAndSubtraction() { - var a = G2Projective.Generator.Double().Double(); // 4P - var b = G2Projective.Generator.Double(); // 2P - var c = a + b; + var a = G2Projective.Generator.Double(); + Assert.AreEqual(G2Projective.Identity, a + (-a)); + Assert.AreEqual(a - a, a + (-a)); + } - var d = G2Projective.Generator; - for (int i = 0; i < 5; i++) + [TestMethod] + public void TestAffineNegationAndSubtraction() + { + var a = G2Affine.Generator; + Assert.AreEqual(G2Projective.Identity, new G2Projective(a) + (-a)); + Assert.AreEqual(new G2Projective(a) - a, new G2Projective(a) + (-a)); + } + + [TestMethod] + public void TestProjectiveScalarMultiplication() + { + var g = G2Projective.Generator; + var a = Scalar.FromRaw(new ulong[] { - d += G2Affine.Generator; - } - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); - Assert.IsFalse(d.IsIdentity); - Assert.IsTrue(d.IsOnCurve); - Assert.AreEqual(c, d); + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(g * c, g * a * b); } - // Degenerate case + [TestMethod] + public void TestAffineScalarMultiplication() { - var beta = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xcd03_c9e4_8671_f071, - 0x5dab_2246_1fcd_a5d2, - 0x5870_42af_d385_1b95, - 0x8eb6_0ebe_01ba_cb9e, - 0x03f9_7d6e_83d0_50d2, - 0x18f0_2065_5463_8741 - }), Fp.Zero); - beta = beta.Square(); - var _a = G2Projective.Generator.Double().Double(); - var b = new G2Projective(_a.X * beta, -_a.Y, in _a.Z); - var a = new G2Affine(_a); - Assert.IsTrue((a.IsOnCurve)); - Assert.IsTrue((b.IsOnCurve)); - - var c = a + b; - Assert.AreEqual(new G2Affine(new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2 - }), Fp.FromRawUnchecked(new ulong[] + var g = G2Affine.Generator; + var a = Scalar.FromRaw(new ulong[] + { + 0x2b56_8297_a56d_a71c, + 0xd8c3_9ecb_0ef3_75d1, + 0x435c_38da_67bf_bf96, + 0x8088_a050_26b6_59b2 + }); + var b = Scalar.FromRaw(new ulong[] + { + 0x785f_dd9b_26ef_8b85, + 0xc997_f258_3769_5c18, + 0x4c8d_bc39_e7b7_56c1, + 0x70d9_b6cc_6d87_df20 + }); + var c = a * b; + + Assert.AreEqual(g * c, new G2Affine(g * a) * b); + } + + [TestMethod] + public void TestIsTorsionFree() + { + var a = new G2Affine(new Fp2(Fp.FromRawUnchecked(new ulong[] { - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952 + 0x89f5_50c8_13db_6431, + 0xa50b_e8c4_56cd_8a1a, + 0xa45b_3741_14ca_e851, + 0xbb61_90f5_bf7f_ff63, + 0x970c_a02c_3ba8_0bc7, + 0x02b8_5d24_e840_fbac + }), + Fp.FromRawUnchecked(new ulong[] + { + 0x6888_bc53_d707_16dc, + 0x3dea_6b41_1768_2d70, + 0xd8f5_f930_500c_a354, + 0x6b5e_cb65_56f5_c155, + 0xc96b_ef04_3477_8ab0, + 0x0508_1505_5150_06ad })), new Fp2(Fp.FromRawUnchecked(new ulong[] { - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3 + 0x3cf1_ea0d_434b_0f40, + 0x1a0d_c610_e603_e333, + 0x7f89_9561_60c7_2fa0, + 0x25ee_03de_cf64_31c5, + 0xeee8_e206_ec0f_e137, + 0x0975_92b2_26df_ef28 }), Fp.FromRawUnchecked(new ulong[] { - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe - })), Fp2.One)), new G2Affine(c)); - Assert.IsFalse(c.IsIdentity); - Assert.IsTrue(c.IsOnCurve); + 0x71e8_bb5f_2924_7367, + 0xa5fe_049e_2118_31ce, + 0x0ce6_b354_502a_3896, + 0x93b0_1200_0997_314e, + 0x6759_f3b6_aa5b_42ac, + 0x1569_44c4_dfe9_2bbb + }))); + Assert.IsFalse(a.IsTorsionFree); + + Assert.IsTrue(G2Affine.Identity.IsTorsionFree); + Assert.IsTrue(G2Affine.Generator.IsTorsionFree); } - } - - [TestMethod] - public void TestProjectiveNegationAndSubtraction() - { - var a = G2Projective.Generator.Double(); - Assert.AreEqual(G2Projective.Identity, a + (-a)); - Assert.AreEqual(a - a, a + (-a)); - } - [TestMethod] - public void TestAffineNegationAndSubtraction() - { - var a = G2Affine.Generator; - Assert.AreEqual(G2Projective.Identity, new G2Projective(a) + (-a)); - Assert.AreEqual(new G2Projective(a) - a, new G2Projective(a) + (-a)); - } - - [TestMethod] - public void TestProjectiveScalarMultiplication() - { - var g = G2Projective.Generator; - var a = Scalar.FromRaw(new ulong[] + [TestMethod] + public void TestMulByX() { - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2 - }); - var b = Scalar.FromRaw(new ulong[] - { - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20 - }); - var c = a * b; - - Assert.AreEqual(g * c, g * a * b); - } + // multiplying by `x` a point in G2 is the same as multiplying by + // the equivalent scalar. + var generator = G2Projective.Generator; + var x = BLS_X_IS_NEGATIVE ? -new Scalar(BLS_X) : new Scalar(BLS_X); + Assert.AreEqual(generator * x, generator.MulByX()); - [TestMethod] - public void TestAffineScalarMultiplication() - { - var g = G2Affine.Generator; - var a = Scalar.FromRaw(new ulong[] - { - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2 - }); - var b = Scalar.FromRaw(new ulong[] - { - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20 - }); - var c = a * b; - - Assert.AreEqual(g * c, new G2Affine(g * a) * b); - } + var point = G2Projective.Generator * new Scalar(42); + Assert.AreEqual(point * x, point.MulByX()); + } - [TestMethod] - public void TestIsTorsionFree() - { - var a = new G2Affine(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x89f5_50c8_13db_6431, - 0xa50b_e8c4_56cd_8a1a, - 0xa45b_3741_14ca_e851, - 0xbb61_90f5_bf7f_ff63, - 0x970c_a02c_3ba8_0bc7, - 0x02b8_5d24_e840_fbac - }), - Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestPsi() { - 0x6888_bc53_d707_16dc, - 0x3dea_6b41_1768_2d70, - 0xd8f5_f930_500c_a354, - 0x6b5e_cb65_56f5_c155, - 0xc96b_ef04_3477_8ab0, - 0x0508_1505_5150_06ad - })), new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x3cf1_ea0d_434b_0f40, - 0x1a0d_c610_e603_e333, - 0x7f89_9561_60c7_2fa0, - 0x25ee_03de_cf64_31c5, - 0xeee8_e206_ec0f_e137, - 0x0975_92b2_26df_ef28 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x71e8_bb5f_2924_7367, - 0xa5fe_049e_2118_31ce, - 0x0ce6_b354_502a_3896, - 0x93b0_1200_0997_314e, - 0x6759_f3b6_aa5b_42ac, - 0x1569_44c4_dfe9_2bbb - }))); - Assert.IsFalse(a.IsTorsionFree); - - Assert.IsTrue(G2Affine.Identity.IsTorsionFree); - Assert.IsTrue(G2Affine.Generator.IsTorsionFree); - } + var generator = G2Projective.Generator; - [TestMethod] - public void TestMulByX() - { - // multiplying by `x` a point in G2 is the same as multiplying by - // the equivalent scalar. - var generator = G2Projective.Generator; - var x = BLS_X_IS_NEGATIVE ? -new Scalar(BLS_X) : new Scalar(BLS_X); - Assert.AreEqual(generator * x, generator.MulByX()); - - var point = G2Projective.Generator * new Scalar(42); - Assert.AreEqual(point * x, point.MulByX()); - } - - [TestMethod] - public void TestPsi() - { - var generator = G2Projective.Generator; + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3 + })); + + // `point` is a random point in the curve + var point = new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc + }), Fp.FromRawUnchecked(new ulong[] + { + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495 + })) * z, new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d + })), z.Square() * z); + Assert.IsTrue(point.IsOnCurve); + + // psi2(P) = psi(psi(P)) + Assert.AreEqual(generator.Psi2(), generator.Psi().Psi()); + Assert.AreEqual(point.Psi2(), point.Psi().Psi()); + // psi(P) is a morphism + Assert.AreEqual(generator.Double().Psi(), generator.Psi().Double()); + Assert.AreEqual(point.Psi() + generator.Psi(), (point + generator).Psi()); + // psi(P) behaves in the same way on the same projective point + var normalized_points = new G2Affine[1]; + G2Projective.BatchNormalize(new[] { point }, normalized_points); + var normalized_point = new G2Projective(normalized_points[0]); + Assert.AreEqual(point.Psi(), normalized_point.Psi()); + Assert.AreEqual(point.Psi2(), normalized_point.Psi2()); + } - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3 - })); - - // `point` is a random point in the curve - var point = new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc - }), Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestClearCofactor() { - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495 - })) * z, new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d - })), z.Square() * z); - Assert.IsTrue(point.IsOnCurve); - - // psi2(P) = psi(psi(P)) - Assert.AreEqual(generator.Psi2(), generator.Psi().Psi()); - Assert.AreEqual(point.Psi2(), point.Psi().Psi()); - // psi(P) is a morphism - Assert.AreEqual(generator.Double().Psi(), generator.Psi().Double()); - Assert.AreEqual(point.Psi() + generator.Psi(), (point + generator).Psi()); - // psi(P) behaves in the same way on the same projective point - var normalized_points = new G2Affine[1]; - G2Projective.BatchNormalize(new[] { point }, normalized_points); - var normalized_point = new G2Projective(normalized_points[0]); - Assert.AreEqual(point.Psi(), normalized_point.Psi()); - Assert.AreEqual(point.Psi2(), normalized_point.Psi2()); - } + var z = new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52 + }), Fp.FromRawUnchecked(new ulong[] + { + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3 + })); + + // `point` is a random point in the curve + var point = new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc + }), Fp.FromRawUnchecked(new ulong[] + { + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495 + })) * z, new Fp2(Fp.FromRawUnchecked(new ulong[] + { + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771 + }), Fp.FromRawUnchecked(new ulong[] + { + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d + })), z.Square() * z); + + Assert.IsTrue(point.IsOnCurve); + Assert.IsFalse(new G2Affine(point).IsTorsionFree); + var cleared_point = point.ClearCofactor(); + + Assert.IsTrue(cleared_point.IsOnCurve); + Assert.IsTrue(new G2Affine(cleared_point).IsTorsionFree); + + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + var generator = G2Projective.Generator; + Assert.IsTrue(generator.ClearCofactor().IsOnCurve); + var id = G2Projective.Identity; + Assert.IsTrue(id.ClearCofactor().IsOnCurve); + + // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| + // h_eff % q = 0x2b116900400069009a40200040001ffff + byte[] h_eff_modq = + { + 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, + 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + Assert.AreEqual(generator * h_eff_modq, generator.ClearCofactor()); + Assert.AreEqual(cleared_point * h_eff_modq, cleared_point.ClearCofactor()); + } - [TestMethod] - public void TestClearCofactor() - { - var z = new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52 - }), Fp.FromRawUnchecked(new ulong[] - { - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3 - })); - - // `point` is a random point in the curve - var point = new G2Projective(new Fp2(Fp.FromRawUnchecked(new ulong[] - { - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc - }), Fp.FromRawUnchecked(new ulong[] - { - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495 - })) * z, new Fp2(Fp.FromRawUnchecked(new ulong[] + [TestMethod] + public void TestBatchNormalize() { - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771 - }), Fp.FromRawUnchecked(new ulong[] - { - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d - })), z.Square() * z); - - Assert.IsTrue(point.IsOnCurve); - Assert.IsFalse(new G2Affine(point).IsTorsionFree); - var cleared_point = point.ClearCofactor(); - - Assert.IsTrue(cleared_point.IsOnCurve); - Assert.IsTrue(new G2Affine(cleared_point).IsTorsionFree); - - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - var generator = G2Projective.Generator; - Assert.IsTrue(generator.ClearCofactor().IsOnCurve); - var id = G2Projective.Identity; - Assert.IsTrue(id.ClearCofactor().IsOnCurve); - - // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| - // h_eff % q = 0x2b116900400069009a40200040001ffff - byte[] h_eff_modq = - { - 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, - 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 - }; - Assert.AreEqual(generator * h_eff_modq, generator.ClearCofactor()); - Assert.AreEqual(cleared_point * h_eff_modq, cleared_point.ClearCofactor()); - } + var a = G2Projective.Generator.Double(); + var b = a.Double(); + var c = b.Double(); - [TestMethod] - public void TestBatchNormalize() - { - var a = G2Projective.Generator.Double(); - var b = a.Double(); - var c = b.Double(); - - foreach (bool a_identity in new[] { false, true }) - { - foreach (bool b_identity in new[] { false, true }) + foreach (bool a_identity in new[] { false, true }) { - foreach (bool c_identity in new[] { false, true }) + foreach (bool b_identity in new[] { false, true }) { - var v = new[] { a, b, c }; - if (a_identity) + foreach (bool c_identity in new[] { false, true }) { - v[0] = G2Projective.Identity; + var v = new[] { a, b, c }; + if (a_identity) + { + v[0] = G2Projective.Identity; + } + if (b_identity) + { + v[1] = G2Projective.Identity; + } + if (c_identity) + { + v[2] = G2Projective.Identity; + } + + var t = new G2Affine[3]; + var expected = new[] { new G2Affine(v[0]), new G2Affine(v[1]), new G2Affine(v[2]) }; + + G2Projective.BatchNormalize(v, t); + + CollectionAssert.AreEqual(t, expected); } - if (b_identity) - { - v[1] = G2Projective.Identity; - } - if (c_identity) - { - v[2] = G2Projective.Identity; - } - - var t = new G2Affine[3]; - var expected = new[] { new G2Affine(v[0]), new G2Affine(v[1]), new G2Affine(v[2]) }; - - G2Projective.BatchNormalize(v, t); - - CollectionAssert.AreEqual(t, expected); } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs index 9019477a8f..eb394446e1 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Pairings.cs @@ -9,61 +9,62 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_Pairings +namespace Neo.Cryptography.BLS12_381.Tests { - [TestMethod] - public void TestGtGenerator() + [TestClass] + public class UT_Pairings { - Assert.AreEqual( - Gt.Generator, - Bls12.Pairing(in G1Affine.Generator, in G2Affine.Generator) - ); - } + [TestMethod] + public void TestGtGenerator() + { + Assert.AreEqual( + Gt.Generator, + Bls12.Pairing(in G1Affine.Generator, in G2Affine.Generator) + ); + } - [TestMethod] - public void TestBilinearity() - { - var a = Scalar.FromRaw(new ulong[] { 1, 2, 3, 4 }).Invert().Square(); - var b = Scalar.FromRaw(new ulong[] { 5, 6, 7, 8 }).Invert().Square(); - var c = a * b; + [TestMethod] + public void TestBilinearity() + { + var a = Scalar.FromRaw(new ulong[] { 1, 2, 3, 4 }).Invert().Square(); + var b = Scalar.FromRaw(new ulong[] { 5, 6, 7, 8 }).Invert().Square(); + var c = a * b; - var g = new G1Affine(G1Affine.Generator * a); - var h = new G2Affine(G2Affine.Generator * b); - var p = Bls12.Pairing(in g, in h); + var g = new G1Affine(G1Affine.Generator * a); + var h = new G2Affine(G2Affine.Generator * b); + var p = Bls12.Pairing(in g, in h); - Assert.AreNotEqual(Gt.Identity, p); + Assert.AreNotEqual(Gt.Identity, p); - var expected = new G1Affine(G1Affine.Generator * c); + var expected = new G1Affine(G1Affine.Generator * c); - Assert.AreEqual(p, Bls12.Pairing(in expected, in G2Affine.Generator)); - Assert.AreEqual( - p, - Bls12.Pairing(in G1Affine.Generator, in G2Affine.Generator) * c - ); - } + Assert.AreEqual(p, Bls12.Pairing(in expected, in G2Affine.Generator)); + Assert.AreEqual( + p, + Bls12.Pairing(in G1Affine.Generator, in G2Affine.Generator) * c + ); + } - [TestMethod] - public void TestUnitary() - { - var g = G1Affine.Generator; - var h = G2Affine.Generator; - var p = -Bls12.Pairing(in g, in h); - var q = Bls12.Pairing(in g, -h); - var r = Bls12.Pairing(-g, in h); + [TestMethod] + public void TestUnitary() + { + var g = G1Affine.Generator; + var h = G2Affine.Generator; + var p = -Bls12.Pairing(in g, in h); + var q = Bls12.Pairing(in g, -h); + var r = Bls12.Pairing(-g, in h); - Assert.AreEqual(p, q); - Assert.AreEqual(q, r); - } + Assert.AreEqual(p, q); + Assert.AreEqual(q, r); + } - [TestMethod] - public void TestMillerLoopResultDefault() - { - Assert.AreEqual( - Gt.Identity, - new MillerLoopResult(Fp12.One).FinalExponentiation() - ); + [TestMethod] + public void TestMillerLoopResultDefault() + { + Assert.AreEqual( + Gt.Identity, + new MillerLoopResult(Fp12.One).FinalExponentiation() + ); + } } } diff --git a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs index 03c1526a7b..3d1ce82f36 100644 --- a/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs +++ b/tests/Neo.Cryptography.BLS12_381.Tests/UT_Scalar.cs @@ -11,401 +11,402 @@ using static Neo.Cryptography.BLS12_381.ScalarConstants; -namespace Neo.Cryptography.BLS12_381.Tests; - -[TestClass] -public class UT_Scalar +namespace Neo.Cryptography.BLS12_381.Tests { - private static readonly Scalar LARGEST = new(new ulong[] - { - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48 - }); - - [TestMethod] - public void TestInv() + [TestClass] + public class UT_Scalar { - // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - var inv = 1ul; - for (int i = 0; i < 63; i++) + private static readonly Scalar LARGEST = new(new ulong[] { - inv = unchecked(inv * inv); - inv = unchecked(inv * MODULUS_LIMBS_64[0]); - } - inv = unchecked(~inv + 1); - - Assert.AreEqual(INV, inv); - } + 0xffff_ffff_0000_0000, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }); - [TestMethod] - public void TestToString() - { - Assert.AreEqual("0x0000000000000000000000000000000000000000000000000000000000000000", Scalar.Zero.ToString()); - Assert.AreEqual("0x0000000000000000000000000000000000000000000000000000000000000001", Scalar.One.ToString()); - Assert.AreEqual("0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe", R2.ToString()); - } + [TestMethod] + public void TestInv() + { + // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating + // by totient(2**64) - 1 - [TestMethod] - public void TestEquality() - { - Assert.AreEqual(Scalar.Zero, Scalar.Zero); - Assert.AreEqual(Scalar.One, Scalar.One); - Assert.AreEqual(R2, R2); + var inv = 1ul; + for (int i = 0; i < 63; i++) + { + inv = unchecked(inv * inv); + inv = unchecked(inv * MODULUS_LIMBS_64[0]); + } + inv = unchecked(~inv + 1); - Assert.AreNotEqual(Scalar.Zero, Scalar.One); - Assert.AreNotEqual(Scalar.One, R2); - } + Assert.AreEqual(INV, inv); + } - [TestMethod] - public void TestToBytes() - { - CollectionAssert.AreEqual(new byte[] + [TestMethod] + public void TestToString() { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - }, Scalar.Zero.ToArray()); + Assert.AreEqual("0x0000000000000000000000000000000000000000000000000000000000000000", Scalar.Zero.ToString()); + Assert.AreEqual("0x0000000000000000000000000000000000000000000000000000000000000001", Scalar.One.ToString()); + Assert.AreEqual("0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe", R2.ToString()); + } - CollectionAssert.AreEqual(new byte[] + [TestMethod] + public void TestEquality() { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - }, Scalar.One.ToArray()); + Assert.AreEqual(Scalar.Zero, Scalar.Zero); + Assert.AreEqual(Scalar.One, Scalar.One); + Assert.AreEqual(R2, R2); - CollectionAssert.AreEqual(new byte[] - { - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - }, R2.ToArray()); + Assert.AreNotEqual(Scalar.Zero, Scalar.One); + Assert.AreNotEqual(Scalar.One, R2); + } - CollectionAssert.AreEqual(new byte[] + [TestMethod] + public void TestToBytes() { - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - }, (-Scalar.One).ToArray()); - } + CollectionAssert.AreEqual(new byte[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + }, Scalar.Zero.ToArray()); - [TestMethod] - public void TestFromBytes() - { - Assert.AreEqual(Scalar.Zero, Scalar.FromBytes(new byte[] - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - })); + CollectionAssert.AreEqual(new byte[] + { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + }, Scalar.One.ToArray()); - Assert.AreEqual(Scalar.One, Scalar.FromBytes(new byte[] - { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - })); + CollectionAssert.AreEqual(new byte[] + { + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + }, R2.ToArray()); - Assert.AreEqual(R2, Scalar.FromBytes(new byte[] - { - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - })); + CollectionAssert.AreEqual(new byte[] + { + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + }, (-Scalar.One).ToArray()); + } - // -1 should work - Scalar.FromBytes(new byte[] + [TestMethod] + public void TestFromBytes() { - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - }); + Assert.AreEqual(Scalar.Zero, Scalar.FromBytes(new byte[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + })); - // modulus is invalid - Assert.ThrowsException(() => Scalar.FromBytes(new byte[] - { - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - })); + Assert.AreEqual(Scalar.One, Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 + })); - // Anything larger than the modulus is invalid - Assert.ThrowsException(() => Scalar.FromBytes(new byte[] - { - 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - })); - Assert.ThrowsException(() => Scalar.FromBytes(new byte[] - { - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 - })); - Assert.ThrowsException(() => Scalar.FromBytes(new byte[] - { - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 - })); - } + Assert.AreEqual(R2, Scalar.FromBytes(new byte[] + { + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 + })); - [TestMethod] - public void TestFromBytesWideR2() - { - Assert.AreEqual(R2, Scalar.FromBytesWide(new byte[] - { - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - })); - } + // -1 should work + Scalar.FromBytes(new byte[] + { + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + }); - [TestMethod] - public void TestFromBytesWideNegativeOne() - { - Assert.AreEqual(-Scalar.One, Scalar.FromBytesWide(new byte[] + // modulus is invalid + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + })); + + // Anything larger than the modulus is invalid + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 + })); + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 + })); + Assert.ThrowsException(() => Scalar.FromBytes(new byte[] + { + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 + })); + } + + [TestMethod] + public void TestFromBytesWideR2() { - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - })); - } + Assert.AreEqual(R2, Scalar.FromBytesWide(new byte[] + { + 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, + 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + })); + } - [TestMethod] - public void TestFromBytesWideMaximum() - { - Assert.AreEqual(new Scalar(new ulong[] + [TestMethod] + public void TestFromBytesWideNegativeOne() { - 0xc62c_1805_439b_73b1, - 0xc2b9_551e_8ced_218e, - 0xda44_ec81_daf9_a422, - 0x5605_aa60_1c16_2e79 - }), Scalar.FromBytesWide(Enumerable.Repeat(0xff, 64).ToArray())); - } + Assert.AreEqual(-Scalar.One, Scalar.FromBytesWide(new byte[] + { + 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + })); + } - [TestMethod] - public void TestZero() - { - Assert.AreEqual(Scalar.Zero, -Scalar.Zero); - Assert.AreEqual(Scalar.Zero, Scalar.Zero + Scalar.Zero); - Assert.AreEqual(Scalar.Zero, Scalar.Zero - Scalar.Zero); - Assert.AreEqual(Scalar.Zero, Scalar.Zero * Scalar.Zero); - } + [TestMethod] + public void TestFromBytesWideMaximum() + { + Assert.AreEqual(new Scalar(new ulong[] + { + 0xc62c_1805_439b_73b1, + 0xc2b9_551e_8ced_218e, + 0xda44_ec81_daf9_a422, + 0x5605_aa60_1c16_2e79 + }), Scalar.FromBytesWide(Enumerable.Repeat(0xff, 64).ToArray())); + } - [TestMethod] - public void TestAddition() - { - var tmp = LARGEST; - tmp += LARGEST; + [TestMethod] + public void TestZero() + { + Assert.AreEqual(Scalar.Zero, -Scalar.Zero); + Assert.AreEqual(Scalar.Zero, Scalar.Zero + Scalar.Zero); + Assert.AreEqual(Scalar.Zero, Scalar.Zero - Scalar.Zero); + Assert.AreEqual(Scalar.Zero, Scalar.Zero * Scalar.Zero); + } - Assert.AreEqual(new Scalar(new ulong[] + [TestMethod] + public void TestAddition() { - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48 - }), tmp); + var tmp = LARGEST; + tmp += LARGEST; - tmp = LARGEST; - tmp += new Scalar(new ulong[] { 1, 0, 0, 0 }); + Assert.AreEqual(new Scalar(new ulong[] + { + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }), tmp); - Assert.AreEqual(Scalar.Zero, tmp); - } + tmp = LARGEST; + tmp += new Scalar(new ulong[] { 1, 0, 0, 0 }); - [TestMethod] - public void TestNegation() - { - var tmp = -LARGEST; + Assert.AreEqual(Scalar.Zero, tmp); + } - Assert.AreEqual(new Scalar(new ulong[] { 1, 0, 0, 0 }), tmp); + [TestMethod] + public void TestNegation() + { + var tmp = -LARGEST; - tmp = -Scalar.Zero; - Assert.AreEqual(Scalar.Zero, tmp); - tmp = -new Scalar(new ulong[] { 1, 0, 0, 0 }); - Assert.AreEqual(LARGEST, tmp); - } + Assert.AreEqual(new Scalar(new ulong[] { 1, 0, 0, 0 }), tmp); - [TestMethod] - public void TestSubtraction() - { - var tmp = LARGEST; - tmp -= LARGEST; + tmp = -Scalar.Zero; + Assert.AreEqual(Scalar.Zero, tmp); + tmp = -new Scalar(new ulong[] { 1, 0, 0, 0 }); + Assert.AreEqual(LARGEST, tmp); + } - Assert.AreEqual(Scalar.Zero, tmp); + [TestMethod] + public void TestSubtraction() + { + var tmp = LARGEST; + tmp -= LARGEST; - tmp = Scalar.Zero; - tmp -= LARGEST; + Assert.AreEqual(Scalar.Zero, tmp); - var tmp2 = MODULUS; - tmp2 -= LARGEST; + tmp = Scalar.Zero; + tmp -= LARGEST; - Assert.AreEqual(tmp, tmp2); - } + var tmp2 = MODULUS; + tmp2 -= LARGEST; - [TestMethod] - public void TestMultiplication() - { - var cur = LARGEST; + Assert.AreEqual(tmp, tmp2); + } - for (int i = 0; i < 100; i++) + [TestMethod] + public void TestMultiplication() { - var tmp = cur; - tmp *= cur; - - var tmp2 = Scalar.Zero; - foreach (bool b in cur - .ToArray() - .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) - .Reverse()) - { - var tmp3 = tmp2; - tmp2 += tmp3; + var cur = LARGEST; - if (b) + for (int i = 0; i < 100; i++) + { + var tmp = cur; + tmp *= cur; + + var tmp2 = Scalar.Zero; + foreach (bool b in cur + .ToArray() + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse()) { - tmp2 += cur; + var tmp3 = tmp2; + tmp2 += tmp3; + + if (b) + { + tmp2 += cur; + } } - } - Assert.AreEqual(tmp, tmp2); + Assert.AreEqual(tmp, tmp2); - cur += LARGEST; + cur += LARGEST; + } } - } - [TestMethod] - public void TestSquaring() - { - var cur = LARGEST; - - for (int i = 0; i < 100; i++) + [TestMethod] + public void TestSquaring() { - var tmp = cur; - tmp = tmp.Square(); - - var tmp2 = Scalar.Zero; - foreach (bool b in cur - .ToArray() - .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) - .Reverse()) - { - var tmp3 = tmp2; - tmp2 += tmp3; + var cur = LARGEST; - if (b) + for (int i = 0; i < 100; i++) + { + var tmp = cur; + tmp = tmp.Square(); + + var tmp2 = Scalar.Zero; + foreach (bool b in cur + .ToArray() + .SelectMany(p => Enumerable.Range(0, 8).Select(q => ((p >> q) & 1) == 1)) + .Reverse()) { - tmp2 += cur; + var tmp3 = tmp2; + tmp2 += tmp3; + + if (b) + { + tmp2 += cur; + } } - } - Assert.AreEqual(tmp, tmp2); + Assert.AreEqual(tmp, tmp2); - cur += LARGEST; + cur += LARGEST; + } } - } - [TestMethod] - public void TestInversion() - { - Assert.ThrowsException(() => Scalar.Zero.Invert()); - Assert.AreEqual(Scalar.One, Scalar.One.Invert()); - Assert.AreEqual(-Scalar.One, (-Scalar.One).Invert()); + [TestMethod] + public void TestInversion() + { + Assert.ThrowsException(() => Scalar.Zero.Invert()); + Assert.AreEqual(Scalar.One, Scalar.One.Invert()); + Assert.AreEqual(-Scalar.One, (-Scalar.One).Invert()); - var tmp = R2; + var tmp = R2; - for (int i = 0; i < 100; i++) - { - var tmp2 = tmp.Invert(); - tmp2 *= tmp; + for (int i = 0; i < 100; i++) + { + var tmp2 = tmp.Invert(); + tmp2 *= tmp; - Assert.AreEqual(Scalar.One, tmp2); + Assert.AreEqual(Scalar.One, tmp2); - tmp += R2; + tmp += R2; + } } - } - [TestMethod] - public void TestInvertIsPow() - { - ulong[] q_minus_2 = + [TestMethod] + public void TestInvertIsPow() { - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48 - }; + ulong[] q_minus_2 = + { + 0xffff_fffe_ffff_ffff, + 0x53bd_a402_fffe_5bfe, + 0x3339_d808_09a1_d805, + 0x73ed_a753_299d_7d48 + }; - var r1 = R; - var r2 = R; - var r3 = R; + var r1 = R; + var r2 = R; + var r3 = R; - for (int i = 0; i < 100; i++) - { - r1 = r1.Invert(); - r2 = r2.PowVartime(q_minus_2); - r3 = r3.Pow(q_minus_2); - - Assert.AreEqual(r1, r2); - Assert.AreEqual(r2, r3); - // Add R so we check something different next time around - r1 += R; - r2 = r1; - r3 = r1; + for (int i = 0; i < 100; i++) + { + r1 = r1.Invert(); + r2 = r2.PowVartime(q_minus_2); + r3 = r3.Pow(q_minus_2); + + Assert.AreEqual(r1, r2); + Assert.AreEqual(r2, r3); + // Add R so we check something different next time around + r1 += R; + r2 = r1; + r3 = r1; + } } - } - - [TestMethod] - public void TestSqrt() - { - Assert.AreEqual(Scalar.Zero.Sqrt(), Scalar.Zero); - var square = new Scalar(new ulong[] + [TestMethod] + public void TestSqrt() { - 0x46cd_85a5_f273_077e, - 0x1d30_c47d_d68f_c735, - 0x77f6_56f6_0bec_a0eb, - 0x494a_a01b_df32_468d - }); - - var none_count = 0; + Assert.AreEqual(Scalar.Zero.Sqrt(), Scalar.Zero); - for (int i = 0; i < 100; i++) - { - Scalar square_root; - try + var square = new Scalar(new ulong[] { - square_root = square.Sqrt(); - Assert.AreEqual(square, square_root * square_root); - } - catch (ArithmeticException) + 0x46cd_85a5_f273_077e, + 0x1d30_c47d_d68f_c735, + 0x77f6_56f6_0bec_a0eb, + 0x494a_a01b_df32_468d + }); + + var none_count = 0; + + for (int i = 0; i < 100; i++) { - none_count++; + Scalar square_root; + try + { + square_root = square.Sqrt(); + Assert.AreEqual(square, square_root * square_root); + } + catch (ArithmeticException) + { + none_count++; + } + square -= Scalar.One; } - square -= Scalar.One; - } - Assert.AreEqual(49, none_count); - } + Assert.AreEqual(49, none_count); + } - [TestMethod] - public void TestFromRaw() - { - Assert.AreEqual(Scalar.FromRaw(new ulong[] + [TestMethod] + public void TestFromRaw() { - 0x0001_ffff_fffd, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f - }), Scalar.FromRaw(Enumerable.Repeat(0xffff_ffff_ffff_ffff, 4).ToArray())); + Assert.AreEqual(Scalar.FromRaw(new ulong[] + { + 0x0001_ffff_fffd, + 0x5884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff5, + 0x1824_b159_acc5_056f + }), Scalar.FromRaw(Enumerable.Repeat(0xffff_ffff_ffff_ffff, 4).ToArray())); - Assert.AreEqual(Scalar.Zero, Scalar.FromRaw(MODULUS_LIMBS_64)); + Assert.AreEqual(Scalar.Zero, Scalar.FromRaw(MODULUS_LIMBS_64)); - Assert.AreEqual(R, Scalar.FromRaw(new ulong[] { 1, 0, 0, 0 })); - } + Assert.AreEqual(R, Scalar.FromRaw(new ulong[] { 1, 0, 0, 0 })); + } - [TestMethod] - public void TestDouble() - { - var a = Scalar.FromRaw(new ulong[] + [TestMethod] + public void TestDouble() { - 0x1fff_3231_233f_fffd, - 0x4884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff3, - 0x1824_b159_acc5_0562 - }); + var a = Scalar.FromRaw(new ulong[] + { + 0x1fff_3231_233f_fffd, + 0x4884_b7fa_0003_4802, + 0x998c_4fef_ecbc_4ff3, + 0x1824_b159_acc5_0562 + }); - Assert.AreEqual(a + a, a.Double()); + Assert.AreEqual(a + a, a.Double()); + } } } diff --git a/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj b/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj index ed23c8a909..84d6a34755 100644 --- a/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj +++ b/tests/Neo.Cryptography.MPTTrie.Tests/Neo.Cryptography.MPTTrie.Tests.csproj @@ -1,14 +1,14 @@ - net8.0 + net9.0 Neo.Cryptography.MPT.Tests - - - + + + diff --git a/tests/Neo.Extensions.Tests/Collections/UT_CollectionExtensions.cs b/tests/Neo.Extensions.Tests/Collections/UT_CollectionExtensions.cs new file mode 100644 index 0000000000..4860d48938 --- /dev/null +++ b/tests/Neo.Extensions.Tests/Collections/UT_CollectionExtensions.cs @@ -0,0 +1,83 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_CollectionExtensions.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 System; +using System.Collections.Generic; + +namespace Neo.Extensions.Tests.Collections +{ + [TestClass] + public class UT_CollectionExtensions + { + [TestMethod] + public void TestChunk() + { + var source = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var chunks = source.Chunk(3).GetEnumerator(); + + Assert.IsTrue(chunks.MoveNext()); + CollectionAssert.AreEqual(new[] { 1, 2, 3 }, chunks.Current); + + Assert.IsTrue(chunks.MoveNext()); + CollectionAssert.AreEqual(new[] { 4, 5, 6 }, chunks.Current); + + Assert.IsTrue(chunks.MoveNext()); + CollectionAssert.AreEqual(new[] { 7, 8, 9 }, chunks.Current); + + Assert.IsTrue(chunks.MoveNext()); + CollectionAssert.AreEqual(new[] { 10 }, chunks.Current); + + // Empty source + var empty = new List(); + var emptyChunks = empty.Chunk(3).GetEnumerator(); + Assert.IsFalse(emptyChunks.MoveNext()); + + // Zero chunk size + var zero = new List { 1, 2, 3 }; + var zeroChunks = zero.Chunk(0).GetEnumerator(); + Assert.ThrowsException(() => zeroChunks.MoveNext()); + + // Null source + IReadOnlyCollection? nullSource = null; + var nullChunks = nullSource.Chunk(3).GetEnumerator(); + Assert.IsFalse(emptyChunks.MoveNext()); + + // HashSet + var hashSet = new HashSet { 1, 2, 3, 4 }; + chunks = hashSet.Chunk(3).GetEnumerator(); + + Assert.IsTrue(chunks.MoveNext()); + CollectionAssert.AreEqual(new[] { 1, 2, 3 }, chunks.Current); + + Assert.IsTrue(chunks.MoveNext()); + CollectionAssert.AreEqual(new[] { 4 }, chunks.Current); + } + + [TestMethod] + public void TestRemoveWhere() + { + var dict = new Dictionary + { + [1] = "a", + [2] = "b", + [3] = "c" + }; + + dict.RemoveWhere(p => p.Value == "b"); + + Assert.AreEqual(2, dict.Count); + Assert.IsFalse(dict.ContainsKey(2)); + Assert.AreEqual("a", dict[1]); + Assert.AreEqual("c", dict[3]); + } + } +} diff --git a/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj b/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj index 03809a4fe3..f15a1eb422 100644 --- a/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj +++ b/tests/Neo.Extensions.Tests/Neo.Extensions.Tests.csproj @@ -1,14 +1,14 @@ - net8.0 + net9.0 enable - - - + + + diff --git a/tests/Neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs b/tests/Neo.Extensions.Tests/UT_ByteArrayEqualityComparer.cs similarity index 98% rename from tests/Neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs rename to tests/Neo.Extensions.Tests/UT_ByteArrayEqualityComparer.cs index ea5ae86be4..dce9720028 100644 --- a/tests/Neo.UnitTests/IO/UT_ByteArrayEqualityComparer.cs +++ b/tests/Neo.Extensions.Tests/UT_ByteArrayEqualityComparer.cs @@ -14,7 +14,7 @@ using System; using System.Linq; -namespace Neo.UnitTests +namespace Neo.Extensions.Tests { [TestClass] public class UT_ByteArrayEqualityComparer diff --git a/tests/Neo.Extensions.Tests/UT_ByteExtensions.cs b/tests/Neo.Extensions.Tests/UT_ByteExtensions.cs index 3c0c21b8f0..87c4ad9bd8 100644 --- a/tests/Neo.Extensions.Tests/UT_ByteExtensions.cs +++ b/tests/Neo.Extensions.Tests/UT_ByteExtensions.cs @@ -12,6 +12,9 @@ using FluentAssertions; using System; +using System.IO.Hashing; +using System.Linq; +using System.Text; namespace Neo.Extensions.Tests { @@ -22,7 +25,10 @@ public class UT_ByteExtensions public void TestToHexString() { byte[] nullStr = null; - Assert.ThrowsException(() => nullStr.ToHexString()); + Assert.ThrowsException(() => nullStr.ToHexString()); + Assert.ThrowsException(() => nullStr.ToHexString(false)); + Assert.ThrowsException(() => nullStr.ToHexString(true)); + byte[] empty = Array.Empty(); empty.ToHexString().Should().Be(""); empty.ToHexString(false).Should().Be(""); @@ -34,6 +40,13 @@ public void TestToHexString() str1.ToHexString(true).Should().Be("6f656e"); } + [TestMethod] + public void TestXxHash3() + { + byte[] data = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat("Hello, World!^_^", 16 * 1024))); + data.XxHash3_32().Should().Be(HashCode.Combine(XxHash3.HashToUInt64(data, 40343))); + } + [TestMethod] public void TestReadOnlySpanToHexString() { diff --git a/tests/Neo.Extensions.Tests/UT_StringExtensions.cs b/tests/Neo.Extensions.Tests/UT_StringExtensions.cs index 6e823d2493..05e5388dfa 100644 --- a/tests/Neo.Extensions.Tests/UT_StringExtensions.cs +++ b/tests/Neo.Extensions.Tests/UT_StringExtensions.cs @@ -42,5 +42,156 @@ public void TestGetVarSizeString() int result = "AA".GetVarSize(); Assert.AreEqual(3, result); } + + [TestMethod] + public void TestGetVarSizeInt() + { + for (int i = 0; i < 3; i++) + { + if (i == 0) + { + int result = UnsafeData.GetVarSize(1); + Assert.AreEqual(1, result); + } + else if (i == 1) + { + int result = UnsafeData.GetVarSize(0xFFFF); + Assert.AreEqual(3, result); + } + else + { + int result = UnsafeData.GetVarSize(0xFFFFFF); + Assert.AreEqual(5, result); + } + } + } + + [TestMethod] + public void TestGetVarSizeGeneric() + { + for (int i = 0; i < 9; i++) + { + if (i == 0) + { + int result = new UInt160[] { UInt160.Zero }.GetVarSize(); + Assert.AreEqual(21, result); + } + else if (i == 1)//sbyte + { + List initList = new() + { + TestEnum0.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(2, result); + } + else if (i == 2)//byte + { + List initList = new() + { + TestEnum1.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(2, result); + } + else if (i == 3)//short + { + List initList = new() + { + TestEnum2.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(3, result); + } + else if (i == 4)//ushort + { + List initList = new() + { + TestEnum3.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(3, result); + } + else if (i == 5)//int + { + List initList = new() + { + TestEnum4.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(5, result); + } + else if (i == 6)//uint + { + List initList = new() + { + TestEnum5.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(5, result); + } + else if (i == 7)//long + { + List initList = new() + { + TestEnum6.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(9, result); + } + else if (i == 8) + { + List initList = new() + { + 1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = testList.GetVarSize(); + Assert.AreEqual(5, result); + } + } + } + + enum TestEnum0 : sbyte + { + case1 = 1, case2 = 2 + } + + enum TestEnum1 : byte + { + case1 = 1, case2 = 2 + } + + enum TestEnum2 : short + { + case1 = 1, case2 = 2 + } + + enum TestEnum3 : ushort + { + case1 = 1, case2 = 2 + } + + enum TestEnum4 : int + { + case1 = 1, case2 = 2 + } + + enum TestEnum5 : uint + { + case1 = 1, case2 = 2 + } + + enum TestEnum6 : long + { + case1 = 1, case2 = 2 + } } } diff --git a/tests/Neo.Extensions.Tests/UT_UnsafeData.cs b/tests/Neo.Extensions.Tests/UT_UnsafeData.cs index 5c8bb310c3..98562fdfa2 100644 --- a/tests/Neo.Extensions.Tests/UT_UnsafeData.cs +++ b/tests/Neo.Extensions.Tests/UT_UnsafeData.cs @@ -12,22 +12,33 @@ public class UT_UnsafeData [TestMethod] public void TestGetVarSizeInt() { - for (int i = 0; i < 3; i++) + for (int i = 0; i < 4; i++) { if (i == 0) { int result = UnsafeData.GetVarSize(1); + int old = OldGetVarSize(1); Assert.AreEqual(1, result); + Assert.AreEqual(1, old); } else if (i == 1) { - int result = UnsafeData.GetVarSize(0xFFFF); + int result = UnsafeData.GetVarSize(ushort.MaxValue); + int old = OldGetVarSize(ushort.MaxValue); Assert.AreEqual(3, result); + Assert.AreEqual(3, old); } - else + else if (i == 2) { - int result = UnsafeData.GetVarSize(0xFFFFFF); + int result = UnsafeData.GetVarSize(uint.MaxValue); + int old = OldGetVarSize(int.MaxValue); Assert.AreEqual(5, result); + Assert.AreEqual(5, old); + } + else + { + int result = UnsafeData.GetVarSize(long.MaxValue); + Assert.AreEqual(9, result); } } } @@ -159,5 +170,15 @@ enum TestEnum6 : long { case1 = 1, case2 = 2 } + + public static int OldGetVarSize(int value) + { + if (value < 0xFD) + return sizeof(byte); + else if (value <= ushort.MaxValue) + return sizeof(byte) + sizeof(ushort); + else + return sizeof(byte) + sizeof(uint); + } } } diff --git a/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj b/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj index c931c287a4..9646f59fde 100644 --- a/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj +++ b/tests/Neo.Json.UnitTests/Neo.Json.UnitTests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable @@ -10,9 +10,9 @@ - - - + + + diff --git a/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj b/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj index fc632685b4..acabd9cbd7 100644 --- a/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj +++ b/tests/Neo.Network.RPC.Tests/Neo.Network.RPC.Tests.csproj @@ -1,15 +1,15 @@ - net8.0 + net9.0 Neo.Network.RPC.Tests - - - - + + + + diff --git a/tests/Neo.Network.RPC.Tests/UT_PolicyAPI.cs b/tests/Neo.Network.RPC.Tests/UT_PolicyAPI.cs index 7defa163ad..a05918c83f 100644 --- a/tests/Neo.Network.RPC.Tests/UT_PolicyAPI.cs +++ b/tests/Neo.Network.RPC.Tests/UT_PolicyAPI.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Neo.Extensions; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; diff --git a/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs b/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs index b87960e76d..e0c4751bac 100644 --- a/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs +++ b/tests/Neo.Network.RPC.Tests/UT_RpcClient.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Moq.Protected; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs b/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs index ae025cf65a..b7eecf2298 100644 --- a/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs +++ b/tests/Neo.Network.RPC.Tests/UT_TransactionManager.cs @@ -13,6 +13,7 @@ using Moq; using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P; diff --git a/tests/Neo.Plugins.ApplicationLogs.Tests/Neo.Plugins.ApplicationLogs.Tests.csproj b/tests/Neo.Plugins.ApplicationLogs.Tests/Neo.Plugins.ApplicationLogs.Tests.csproj index 92951a59dd..7543c8487d 100644 --- a/tests/Neo.Plugins.ApplicationLogs.Tests/Neo.Plugins.ApplicationLogs.Tests.csproj +++ b/tests/Neo.Plugins.ApplicationLogs.Tests/Neo.Plugins.ApplicationLogs.Tests.csproj @@ -1,14 +1,17 @@ - net8.0 + net9.0 Neo.Plugins.ApplicationsLogs.Tests - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogReader.cs b/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogReader.cs index f7e2e2bdad..59671cd43b 100644 --- a/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogReader.cs +++ b/tests/Neo.Plugins.ApplicationLogs.Tests/UT_LogReader.cs @@ -13,6 +13,7 @@ using Akka.Util; using Microsoft.AspNetCore.Authorization; using Neo.Cryptography; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Ledger; diff --git a/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs b/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs index c404f3f982..b9e2d407f8 100644 --- a/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs +++ b/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs @@ -14,6 +14,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; diff --git a/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj b/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj index d192f37ea7..a1f18331b8 100644 --- a/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj +++ b/tests/Neo.Plugins.OracleService.Tests/Neo.Plugins.OracleService.Tests.csproj @@ -1,17 +1,17 @@ - net8.0 + net9.0 OracleService.Tests Neo.Plugins - - - - - + + + + + diff --git a/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs b/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs index 160ac0407e..229a5f361d 100644 --- a/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs +++ b/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Akka.Actor; +using Neo.Extensions; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj b/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj index b7e2ec91ac..0dc03fea55 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj +++ b/tests/Neo.Plugins.RpcServer.Tests/Neo.Plugins.RpcServer.Tests.csproj @@ -1,17 +1,17 @@ - net8.0 + net9.0 Neo.Plugins.RpcServer.Tests Neo.Plugins.RpcServer.Tests true - - - - + + + + @@ -19,5 +19,4 @@ - \ No newline at end of file diff --git a/tests/Neo.Plugins.RpcServer.Tests/TestMemoryStoreProvider.cs b/tests/Neo.Plugins.RpcServer.Tests/TestMemoryStoreProvider.cs index b39b4aeae5..db519c4f4b 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/TestMemoryStoreProvider.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/TestMemoryStoreProvider.cs @@ -11,11 +11,12 @@ using Neo.Persistence; -namespace Neo.Plugins.RpcServer.Tests; - -public class TestMemoryStoreProvider(MemoryStore memoryStore) : IStoreProvider +namespace Neo.Plugins.RpcServer.Tests { - public MemoryStore MemoryStore { get; init; } = memoryStore; - public string Name => nameof(MemoryStore); - public IStore GetStore(string path) => MemoryStore; + public class TestMemoryStoreProvider(MemoryStore memoryStore) : IStoreProvider + { + public MemoryStore MemoryStore { get; init; } = memoryStore; + public string Name => nameof(MemoryStore); + public IStore GetStore(string path) => MemoryStore; + } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs index a87f1c7961..48e69cac7d 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_Parameters.cs @@ -16,399 +16,400 @@ using Neo.Wallets; using System; -namespace Neo.Plugins.RpcServer.Tests; - -[TestClass] -public class UT_Parameters +namespace Neo.Plugins.RpcServer.Tests { - [TestMethod] - public void TestTryParse_ContractNameOrHashOrId() - { - Assert.IsTrue(ContractNameOrHashOrId.TryParse("1", out var contractNameOrHashOrId)); - Assert.IsTrue(contractNameOrHashOrId.IsId); - Assert.IsTrue(ContractNameOrHashOrId.TryParse("0x1234567890abcdef1234567890abcdef12345678", out contractNameOrHashOrId)); - Assert.IsTrue(contractNameOrHashOrId.IsHash); - Assert.IsTrue(ContractNameOrHashOrId.TryParse("test", out contractNameOrHashOrId)); - Assert.IsTrue(contractNameOrHashOrId.IsName); - Assert.IsFalse(ContractNameOrHashOrId.TryParse("", out _)); - - JToken token = 1; - Assert.AreEqual(1, ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token, typeof(ContractNameOrHashOrId))).AsId()); - - JToken token2 = "1"; - Assert.AreEqual(1, ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token2, typeof(ContractNameOrHashOrId))).AsId()); - - JToken token3 = "0x1234567890abcdef1234567890abcdef12345678"; - Assert.AreEqual(UInt160.Parse("0x1234567890abcdef1234567890abcdef12345678"), ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token3, typeof(ContractNameOrHashOrId))).AsHash()); - - JToken token4 = "0xabc"; - Assert.ThrowsException(() => ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token4, typeof(ContractNameOrHashOrId))).AsHash()); - } - - [TestMethod] - public void TestTryParse_BlockHashOrIndex() - { - Assert.IsTrue(BlockHashOrIndex.TryParse("1", out var blockHashOrIndex)); - Assert.IsTrue(blockHashOrIndex.IsIndex); - Assert.AreEqual(1u, blockHashOrIndex.AsIndex()); - Assert.IsTrue(BlockHashOrIndex.TryParse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d", out blockHashOrIndex)); - Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), blockHashOrIndex.AsHash()); - Assert.IsFalse(BlockHashOrIndex.TryParse("", out _)); - - JToken token = 1; - Assert.AreEqual(1u, ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token, typeof(BlockHashOrIndex))).AsIndex()); - - JToken token2 = -1; - Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token2, typeof(BlockHashOrIndex))).AsIndex()); - - JToken token3 = "1"; - Assert.AreEqual(1u, ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token3, typeof(BlockHashOrIndex))).AsIndex()); - - JToken token4 = "-1"; - Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token4, typeof(BlockHashOrIndex))).AsIndex()); - - JToken token5 = "0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"; - Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token5, typeof(BlockHashOrIndex))).AsHash()); - - JToken token6 = "761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"; - Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token6, typeof(BlockHashOrIndex))).AsHash()); - - JToken token7 = "0xabc"; - Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token7, typeof(BlockHashOrIndex))).AsHash()); - } - - [TestMethod] - public void TestUInt160() - { - JToken token = "0x1234567890abcdef1234567890abcdef12345678"; - Assert.AreEqual(UInt160.Parse("0x1234567890abcdef1234567890abcdef12345678"), ParameterConverter.ConvertUInt160(token, TestProtocolSettings.Default.AddressVersion)); - - JToken token2 = "0xabc"; - Assert.ThrowsException(() => ParameterConverter.ConvertUInt160(token2, TestProtocolSettings.Default.AddressVersion)); - - JToken token3 = "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf"; - Assert.AreEqual("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(TestProtocolSettings.Default.AddressVersion), ParameterConverter.ConvertUInt160(token3, TestProtocolSettings.Default.AddressVersion)); - } - - [TestMethod] - public void TestUInt256() - { - JToken token = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; - Assert.AreEqual(UInt256.Parse("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"), ParameterConverter.ConvertParameter(token, typeof(UInt256))); - - JToken token2 = "0xabc"; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(UInt256))); - } - - [TestMethod] - public void TestInteger() - { - JToken token = 1; - Assert.AreEqual(1, ParameterConverter.ConvertParameter(token, typeof(int))); - Assert.AreEqual((long)1, ParameterConverter.ConvertParameter(token, typeof(long))); - Assert.AreEqual((uint)1, ParameterConverter.ConvertParameter(token, typeof(uint))); - Assert.AreEqual((ulong)1, ParameterConverter.ConvertParameter(token, typeof(ulong))); - Assert.AreEqual((short)1, ParameterConverter.ConvertParameter(token, typeof(short))); - Assert.AreEqual((ushort)1, ParameterConverter.ConvertParameter(token, typeof(ushort))); - Assert.AreEqual((byte)1, ParameterConverter.ConvertParameter(token, typeof(byte))); - Assert.AreEqual((sbyte)1, ParameterConverter.ConvertParameter(token, typeof(sbyte))); - - JToken token2 = 1.1; - - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(uint))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(ulong))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(short))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(ushort))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(byte))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(sbyte))); - - JToken token3 = "1"; - - Assert.AreEqual((int)1, ParameterConverter.ConvertParameter(token3, typeof(int))); - Assert.AreEqual((long)1, ParameterConverter.ConvertParameter(token3, typeof(long))); - Assert.AreEqual((uint)1, ParameterConverter.ConvertParameter(token3, typeof(uint))); - Assert.AreEqual((ulong)1, ParameterConverter.ConvertParameter(token3, typeof(ulong))); - Assert.AreEqual((short)1, ParameterConverter.ConvertParameter(token3, typeof(short))); - Assert.AreEqual((ushort)1, ParameterConverter.ConvertParameter(token3, typeof(ushort))); - Assert.AreEqual((byte)1, ParameterConverter.ConvertParameter(token3, typeof(byte))); - Assert.AreEqual((sbyte)1, ParameterConverter.ConvertParameter(token3, typeof(sbyte))); - - JToken token4 = "1.1"; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(uint))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(ulong))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(short))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(ushort))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(byte))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(sbyte))); - - JToken token5 = "abc"; - - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(uint))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(ulong))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(short))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(ushort))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(byte))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(sbyte))); - - JToken token6 = -1; - - Assert.AreEqual(-1, ParameterConverter.ConvertParameter(token6, typeof(int))); - Assert.AreEqual((long)-1, ParameterConverter.ConvertParameter(token6, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(uint))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(ulong))); - Assert.AreEqual((short)-1, ParameterConverter.ConvertParameter(token6, typeof(short))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(ushort))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(byte))); - Assert.AreEqual((sbyte)-1, ParameterConverter.ConvertParameter(token6, typeof(sbyte))); - } - - [TestMethod] - public void TestBoolean() - { - JToken token = true; - Assert.AreEqual(true, ParameterConverter.ConvertParameter(token, typeof(bool))); - JToken token2 = false; - Assert.AreEqual(false, ParameterConverter.ConvertParameter(token2, typeof(bool))); - JToken token6 = 1; - Assert.AreEqual(true, ParameterConverter.ConvertParameter(token6, typeof(bool))); - JToken token7 = 0; - Assert.AreEqual(false, ParameterConverter.ConvertParameter(token7, typeof(bool))); - } - - [TestMethod] - public void TestNumericTypeConversions() - { - // Test integer conversions - TestIntegerConversions(); - - // Test byte conversions - TestByteConversions(); - - // Test sbyte conversions - TestSByteConversions(); - - // Test short conversions - TestShortConversions(); - - // Test ushort conversions - TestUShortConversions(); - - // Test uint conversions - TestUIntConversions(); - - // Test long conversions - TestLongConversions(); - - // Test ulong conversions - TestULongConversions(); - } - - private void TestIntegerConversions() - { - // Test max value - JToken maxToken = int.MaxValue; - Assert.AreEqual(int.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(int))); - - // Test min value - JToken minToken = int.MinValue; - Assert.AreEqual(int.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(int))); - - // Test overflow - JToken overflowToken = (long)int.MaxValue + 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(int))); - - // Test underflow - JToken underflowToken = (long)int.MinValue - 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(int))); - } - - private void TestByteConversions() - { - // Test max value - JToken maxToken = byte.MaxValue; - Assert.AreEqual(byte.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(byte))); - - // Test min value - JToken minToken = byte.MinValue; - Assert.AreEqual(byte.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(byte))); - - // Test overflow - JToken overflowToken = (int)byte.MaxValue + 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(byte))); - - // Test underflow - JToken underflowToken = -1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(byte))); - } - - private void TestSByteConversions() - { - // Test max value - JToken maxToken = sbyte.MaxValue; - Assert.AreEqual(sbyte.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(sbyte))); - - // Test min value - JToken minToken = sbyte.MinValue; - Assert.AreEqual(sbyte.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(sbyte))); - - // Test overflow - JToken overflowToken = (int)sbyte.MaxValue + 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(sbyte))); - - // Test underflow - JToken underflowToken = (int)sbyte.MinValue - 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(sbyte))); - } - - private void TestShortConversions() - { - // Test max value - JToken maxToken = short.MaxValue; - Assert.AreEqual(short.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(short))); - - // Test min value - JToken minToken = short.MinValue; - Assert.AreEqual(short.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(short))); - - // Test overflow - JToken overflowToken = (int)short.MaxValue + 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(short))); - - // Test underflow - JToken underflowToken = (int)short.MinValue - 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(short))); - } - - private void TestUShortConversions() + [TestClass] + public class UT_Parameters { - // Test max value - JToken maxToken = ushort.MaxValue; - Assert.AreEqual(ushort.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(ushort))); - - // Test min value - JToken minToken = ushort.MinValue; - Assert.AreEqual(ushort.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(ushort))); - - // Test overflow - JToken overflowToken = (int)ushort.MaxValue + 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(ushort))); - - // Test underflow - JToken underflowToken = -1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(ushort))); - } - - private void TestUIntConversions() - { - // Test max value - JToken maxToken = uint.MaxValue; - Assert.AreEqual(uint.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(uint))); - - // Test min value - JToken minToken = uint.MinValue; - Assert.AreEqual(uint.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(uint))); - - // Test overflow - JToken overflowToken = (ulong)uint.MaxValue + 1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(uint))); - - // Test underflow - JToken underflowToken = -1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(uint))); - } - - private void TestLongConversions() - { - // Test max value - JToken maxToken = JNumber.MAX_SAFE_INTEGER; - Assert.AreEqual(JNumber.MAX_SAFE_INTEGER, ParameterConverter.ConvertParameter(maxToken, typeof(long))); - - // Test min value - JToken minToken = JNumber.MIN_SAFE_INTEGER; - Assert.AreEqual(JNumber.MIN_SAFE_INTEGER, ParameterConverter.ConvertParameter(minToken, typeof(long))); - - // Test overflow - JToken overflowToken = $"{JNumber.MAX_SAFE_INTEGER}0"; // This will be parsed as a string, causing overflow - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(long))); - - // Test underflow - JToken underflowToken = $"-{JNumber.MIN_SAFE_INTEGER}0"; // This will be parsed as a string, causing underflow - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(long))); - } - - private void TestULongConversions() - { - // Test max value - JToken maxToken = JNumber.MAX_SAFE_INTEGER; - Assert.AreEqual((ulong)JNumber.MAX_SAFE_INTEGER, ParameterConverter.ConvertParameter(maxToken, typeof(ulong))); - - // Test min value - JToken minToken = ulong.MinValue; - Assert.AreEqual(ulong.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(ulong))); - - // Test overflow - JToken overflowToken = $"{JNumber.MAX_SAFE_INTEGER}0"; // This will be parsed as a string, causing overflow - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(ulong))); - - // Test underflow - JToken underflowToken = -1; - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(ulong))); - } - - [TestMethod] - public void TestAdditionalEdgeCases() - { - // Test conversion of fractional values slightly less than integers - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(0.9999999999999, typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(-0.0000000000001, typeof(int))); - - // Test conversion of very large double values to integer types - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.MaxValue, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.MinValue, typeof(long))); - - // Test conversion of NaN and Infinity - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.NaN, typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.PositiveInfinity, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.NegativeInfinity, typeof(ulong))); - - // Test conversion of string representations of numbers - Assert.AreEqual(int.MaxValue, ParameterConverter.ConvertParameter(int.MaxValue.ToString(), typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(long.MinValue.ToString(), typeof(long))); - - // Test conversion of hexadecimal string representations - Assert.ThrowsException(() => ParameterConverter.ConvertParameter("0xFF", typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter("0x100", typeof(byte))); - - // Test conversion of whitespace-padded strings - Assert.AreEqual(42, ParameterConverter.ConvertParameter(" 42 ", typeof(int))); - Assert.AreEqual(42, ParameterConverter.ConvertParameter(" 42.0 ", typeof(int))); - - // Test conversion of empty or null values - Assert.AreEqual(0, ParameterConverter.ConvertParameter("", typeof(int))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(JToken.Null, typeof(int))); - - // Test conversion to non-numeric types - Assert.ThrowsException(() => ParameterConverter.ConvertParameter(42, typeof(DateTime))); - - // Test conversion of values just outside the safe integer range for long and ulong - Assert.ThrowsException(() => ParameterConverter.ConvertParameter((double)long.MaxValue, typeof(long))); - Assert.ThrowsException(() => ParameterConverter.ConvertParameter((double)ulong.MaxValue, typeof(ulong))); - - // Test conversion of scientific notation - Assert.AreEqual(1000000, ParameterConverter.ConvertParameter("1e6", typeof(int))); - Assert.AreEqual(150, ParameterConverter.ConvertParameter("1.5e2", typeof(int))); - - // Test conversion of boolean values to numeric types - Assert.AreEqual(1, ParameterConverter.ConvertParameter(true, typeof(int))); - Assert.AreEqual(0, ParameterConverter.ConvertParameter(false, typeof(int))); - - // Test conversion of Unicode numeric characters - Assert.ThrowsException(() => ParameterConverter.ConvertParameter("1234", typeof(int))); + [TestMethod] + public void TestTryParse_ContractNameOrHashOrId() + { + Assert.IsTrue(ContractNameOrHashOrId.TryParse("1", out var contractNameOrHashOrId)); + Assert.IsTrue(contractNameOrHashOrId.IsId); + Assert.IsTrue(ContractNameOrHashOrId.TryParse("0x1234567890abcdef1234567890abcdef12345678", out contractNameOrHashOrId)); + Assert.IsTrue(contractNameOrHashOrId.IsHash); + Assert.IsTrue(ContractNameOrHashOrId.TryParse("test", out contractNameOrHashOrId)); + Assert.IsTrue(contractNameOrHashOrId.IsName); + Assert.IsFalse(ContractNameOrHashOrId.TryParse("", out _)); + + JToken token = 1; + Assert.AreEqual(1, ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token, typeof(ContractNameOrHashOrId))).AsId()); + + JToken token2 = "1"; + Assert.AreEqual(1, ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token2, typeof(ContractNameOrHashOrId))).AsId()); + + JToken token3 = "0x1234567890abcdef1234567890abcdef12345678"; + Assert.AreEqual(UInt160.Parse("0x1234567890abcdef1234567890abcdef12345678"), ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token3, typeof(ContractNameOrHashOrId))).AsHash()); + + JToken token4 = "0xabc"; + Assert.ThrowsException(() => ((ContractNameOrHashOrId)ParameterConverter.ConvertParameter(token4, typeof(ContractNameOrHashOrId))).AsHash()); + } + + [TestMethod] + public void TestTryParse_BlockHashOrIndex() + { + Assert.IsTrue(BlockHashOrIndex.TryParse("1", out var blockHashOrIndex)); + Assert.IsTrue(blockHashOrIndex.IsIndex); + Assert.AreEqual(1u, blockHashOrIndex.AsIndex()); + Assert.IsTrue(BlockHashOrIndex.TryParse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d", out blockHashOrIndex)); + Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), blockHashOrIndex.AsHash()); + Assert.IsFalse(BlockHashOrIndex.TryParse("", out _)); + + JToken token = 1; + Assert.AreEqual(1u, ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token2 = -1; + Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token2, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token3 = "1"; + Assert.AreEqual(1u, ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token3, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token4 = "-1"; + Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token4, typeof(BlockHashOrIndex))).AsIndex()); + + JToken token5 = "0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"; + Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token5, typeof(BlockHashOrIndex))).AsHash()); + + JToken token6 = "761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"; + Assert.AreEqual(UInt256.Parse("0x761a9bb72ca2a63984db0cc43f943a2a25e464f62d1a91114c2b6fbbfd24b51d"), ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token6, typeof(BlockHashOrIndex))).AsHash()); + + JToken token7 = "0xabc"; + Assert.ThrowsException(() => ((BlockHashOrIndex)ParameterConverter.ConvertParameter(token7, typeof(BlockHashOrIndex))).AsHash()); + } + + [TestMethod] + public void TestUInt160() + { + JToken token = "0x1234567890abcdef1234567890abcdef12345678"; + Assert.AreEqual(UInt160.Parse("0x1234567890abcdef1234567890abcdef12345678"), ParameterConverter.ConvertUInt160(token, TestProtocolSettings.Default.AddressVersion)); + + JToken token2 = "0xabc"; + Assert.ThrowsException(() => ParameterConverter.ConvertUInt160(token2, TestProtocolSettings.Default.AddressVersion)); + + JToken token3 = "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf"; + Assert.AreEqual("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(TestProtocolSettings.Default.AddressVersion), ParameterConverter.ConvertUInt160(token3, TestProtocolSettings.Default.AddressVersion)); + } + + [TestMethod] + public void TestUInt256() + { + JToken token = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + Assert.AreEqual(UInt256.Parse("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"), ParameterConverter.ConvertParameter(token, typeof(UInt256))); + + JToken token2 = "0xabc"; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(UInt256))); + } + + [TestMethod] + public void TestInteger() + { + JToken token = 1; + Assert.AreEqual(1, ParameterConverter.ConvertParameter(token, typeof(int))); + Assert.AreEqual((long)1, ParameterConverter.ConvertParameter(token, typeof(long))); + Assert.AreEqual((uint)1, ParameterConverter.ConvertParameter(token, typeof(uint))); + Assert.AreEqual((ulong)1, ParameterConverter.ConvertParameter(token, typeof(ulong))); + Assert.AreEqual((short)1, ParameterConverter.ConvertParameter(token, typeof(short))); + Assert.AreEqual((ushort)1, ParameterConverter.ConvertParameter(token, typeof(ushort))); + Assert.AreEqual((byte)1, ParameterConverter.ConvertParameter(token, typeof(byte))); + Assert.AreEqual((sbyte)1, ParameterConverter.ConvertParameter(token, typeof(sbyte))); + + JToken token2 = 1.1; + + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(ulong))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(byte))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token2, typeof(sbyte))); + + JToken token3 = "1"; + + Assert.AreEqual((int)1, ParameterConverter.ConvertParameter(token3, typeof(int))); + Assert.AreEqual((long)1, ParameterConverter.ConvertParameter(token3, typeof(long))); + Assert.AreEqual((uint)1, ParameterConverter.ConvertParameter(token3, typeof(uint))); + Assert.AreEqual((ulong)1, ParameterConverter.ConvertParameter(token3, typeof(ulong))); + Assert.AreEqual((short)1, ParameterConverter.ConvertParameter(token3, typeof(short))); + Assert.AreEqual((ushort)1, ParameterConverter.ConvertParameter(token3, typeof(ushort))); + Assert.AreEqual((byte)1, ParameterConverter.ConvertParameter(token3, typeof(byte))); + Assert.AreEqual((sbyte)1, ParameterConverter.ConvertParameter(token3, typeof(sbyte))); + + JToken token4 = "1.1"; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(ulong))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(byte))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token4, typeof(sbyte))); + + JToken token5 = "abc"; + + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(ulong))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(byte))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token5, typeof(sbyte))); + + JToken token6 = -1; + + Assert.AreEqual(-1, ParameterConverter.ConvertParameter(token6, typeof(int))); + Assert.AreEqual((long)-1, ParameterConverter.ConvertParameter(token6, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(uint))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(ulong))); + Assert.AreEqual((short)-1, ParameterConverter.ConvertParameter(token6, typeof(short))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(ushort))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(token6, typeof(byte))); + Assert.AreEqual((sbyte)-1, ParameterConverter.ConvertParameter(token6, typeof(sbyte))); + } + + [TestMethod] + public void TestBoolean() + { + JToken token = true; + Assert.AreEqual(true, ParameterConverter.ConvertParameter(token, typeof(bool))); + JToken token2 = false; + Assert.AreEqual(false, ParameterConverter.ConvertParameter(token2, typeof(bool))); + JToken token6 = 1; + Assert.AreEqual(true, ParameterConverter.ConvertParameter(token6, typeof(bool))); + JToken token7 = 0; + Assert.AreEqual(false, ParameterConverter.ConvertParameter(token7, typeof(bool))); + } + + [TestMethod] + public void TestNumericTypeConversions() + { + // Test integer conversions + TestIntegerConversions(); + + // Test byte conversions + TestByteConversions(); + + // Test sbyte conversions + TestSByteConversions(); + + // Test short conversions + TestShortConversions(); + + // Test ushort conversions + TestUShortConversions(); + + // Test uint conversions + TestUIntConversions(); + + // Test long conversions + TestLongConversions(); + + // Test ulong conversions + TestULongConversions(); + } + + private void TestIntegerConversions() + { + // Test max value + JToken maxToken = int.MaxValue; + Assert.AreEqual(int.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(int))); + + // Test min value + JToken minToken = int.MinValue; + Assert.AreEqual(int.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(int))); + + // Test overflow + JToken overflowToken = (long)int.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(int))); + + // Test underflow + JToken underflowToken = (long)int.MinValue - 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(int))); + } + + private void TestByteConversions() + { + // Test max value + JToken maxToken = byte.MaxValue; + Assert.AreEqual(byte.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(byte))); + + // Test min value + JToken minToken = byte.MinValue; + Assert.AreEqual(byte.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(byte))); + + // Test overflow + JToken overflowToken = (int)byte.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(byte))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(byte))); + } + + private void TestSByteConversions() + { + // Test max value + JToken maxToken = sbyte.MaxValue; + Assert.AreEqual(sbyte.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(sbyte))); + + // Test min value + JToken minToken = sbyte.MinValue; + Assert.AreEqual(sbyte.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(sbyte))); + + // Test overflow + JToken overflowToken = (int)sbyte.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(sbyte))); + + // Test underflow + JToken underflowToken = (int)sbyte.MinValue - 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(sbyte))); + } + + private void TestShortConversions() + { + // Test max value + JToken maxToken = short.MaxValue; + Assert.AreEqual(short.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(short))); + + // Test min value + JToken minToken = short.MinValue; + Assert.AreEqual(short.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(short))); + + // Test overflow + JToken overflowToken = (int)short.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(short))); + + // Test underflow + JToken underflowToken = (int)short.MinValue - 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(short))); + } + + private void TestUShortConversions() + { + // Test max value + JToken maxToken = ushort.MaxValue; + Assert.AreEqual(ushort.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(ushort))); + + // Test min value + JToken minToken = ushort.MinValue; + Assert.AreEqual(ushort.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(ushort))); + + // Test overflow + JToken overflowToken = (int)ushort.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(ushort))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(ushort))); + } + + private void TestUIntConversions() + { + // Test max value + JToken maxToken = uint.MaxValue; + Assert.AreEqual(uint.MaxValue, ParameterConverter.ConvertParameter(maxToken, typeof(uint))); + + // Test min value + JToken minToken = uint.MinValue; + Assert.AreEqual(uint.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(uint))); + + // Test overflow + JToken overflowToken = (ulong)uint.MaxValue + 1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(uint))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(uint))); + } + + private void TestLongConversions() + { + // Test max value + JToken maxToken = JNumber.MAX_SAFE_INTEGER; + Assert.AreEqual(JNumber.MAX_SAFE_INTEGER, ParameterConverter.ConvertParameter(maxToken, typeof(long))); + + // Test min value + JToken minToken = JNumber.MIN_SAFE_INTEGER; + Assert.AreEqual(JNumber.MIN_SAFE_INTEGER, ParameterConverter.ConvertParameter(minToken, typeof(long))); + + // Test overflow + JToken overflowToken = $"{JNumber.MAX_SAFE_INTEGER}0"; // This will be parsed as a string, causing overflow + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(long))); + + // Test underflow + JToken underflowToken = $"-{JNumber.MIN_SAFE_INTEGER}0"; // This will be parsed as a string, causing underflow + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(long))); + } + + private void TestULongConversions() + { + // Test max value + JToken maxToken = JNumber.MAX_SAFE_INTEGER; + Assert.AreEqual((ulong)JNumber.MAX_SAFE_INTEGER, ParameterConverter.ConvertParameter(maxToken, typeof(ulong))); + + // Test min value + JToken minToken = ulong.MinValue; + Assert.AreEqual(ulong.MinValue, ParameterConverter.ConvertParameter(minToken, typeof(ulong))); + + // Test overflow + JToken overflowToken = $"{JNumber.MAX_SAFE_INTEGER}0"; // This will be parsed as a string, causing overflow + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(overflowToken, typeof(ulong))); + + // Test underflow + JToken underflowToken = -1; + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(underflowToken, typeof(ulong))); + } + + [TestMethod] + public void TestAdditionalEdgeCases() + { + // Test conversion of fractional values slightly less than integers + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(0.9999999999999, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(-0.0000000000001, typeof(int))); + + // Test conversion of very large double values to integer types + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.MaxValue, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.MinValue, typeof(long))); + + // Test conversion of NaN and Infinity + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.NaN, typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.PositiveInfinity, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(double.NegativeInfinity, typeof(ulong))); + + // Test conversion of string representations of numbers + Assert.AreEqual(int.MaxValue, ParameterConverter.ConvertParameter(int.MaxValue.ToString(), typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(long.MinValue.ToString(), typeof(long))); + + // Test conversion of hexadecimal string representations + Assert.ThrowsException(() => ParameterConverter.ConvertParameter("0xFF", typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter("0x100", typeof(byte))); + + // Test conversion of whitespace-padded strings + Assert.AreEqual(42, ParameterConverter.ConvertParameter(" 42 ", typeof(int))); + Assert.AreEqual(42, ParameterConverter.ConvertParameter(" 42.0 ", typeof(int))); + + // Test conversion of empty or null values + Assert.AreEqual(0, ParameterConverter.ConvertParameter("", typeof(int))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(JToken.Null, typeof(int))); + + // Test conversion to non-numeric types + Assert.ThrowsException(() => ParameterConverter.ConvertParameter(42, typeof(DateTime))); + + // Test conversion of values just outside the safe integer range for long and ulong + Assert.ThrowsException(() => ParameterConverter.ConvertParameter((double)long.MaxValue, typeof(long))); + Assert.ThrowsException(() => ParameterConverter.ConvertParameter((double)ulong.MaxValue, typeof(ulong))); + + // Test conversion of scientific notation + Assert.AreEqual(1000000, ParameterConverter.ConvertParameter("1e6", typeof(int))); + Assert.AreEqual(150, ParameterConverter.ConvertParameter("1.5e2", typeof(int))); + + // Test conversion of boolean values to numeric types + Assert.AreEqual(1, ParameterConverter.ConvertParameter(true, typeof(int))); + Assert.AreEqual(0, ParameterConverter.ConvertParameter(false, typeof(int))); + + // Test conversion of Unicode numeric characters + Assert.ThrowsException(() => ParameterConverter.ConvertParameter("1234", typeof(int))); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_Result.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_Result.cs index aa6a8fe308..8797bc76dd 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_Result.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_Result.cs @@ -12,15 +12,16 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; -namespace Neo.Plugins.RpcServer.Tests; - -[TestClass] -public class UT_Result +namespace Neo.Plugins.RpcServer.Tests { - [TestMethod] - public void TestNotNull_Or() + [TestClass] + public class UT_Result { - ContractState? contracts = null; - Assert.ThrowsException(() => contracts.NotNull_Or(RpcError.UnknownContract).ToJson()); + [TestMethod] + public void TestNotNull_Or() + { + ContractState? contracts = null; + Assert.ThrowsException(() => contracts.NotNull_Or(RpcError.UnknownContract).ToJson()); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs index b04b5e33d4..4a42d084d1 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Blockchain.cs @@ -14,6 +14,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Ledger; diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs index 042bde0d30..c4ab013021 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Node.cs @@ -12,6 +12,7 @@ using Akka.Actor; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P; diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 9a8b87da9a..3b204cc53f 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -14,6 +14,7 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; @@ -30,205 +31,206 @@ using System.Text; using System.Threading; -namespace Neo.Plugins.RpcServer.Tests; - -public partial class UT_RpcServer +namespace Neo.Plugins.RpcServer.Tests { - static readonly string NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; - static readonly string NeoTransferScript = "CxEMFPlu76Cuc\u002BbgteStE4ozsOWTNUdrDBQtYNweHko3YcnMFOes3ceblcI/lRTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I="; - static readonly UInt160 ValidatorScriptHash = Contract - .CreateSignatureRedeemScript(TestProtocolSettings.SoleNode.StandbyCommittee[0]) - .ToScriptHash(); - static readonly string ValidatorAddress = ValidatorScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); - static readonly UInt160 MultisigScriptHash = Contract - .CreateMultiSigRedeemScript(1, TestProtocolSettings.SoleNode.StandbyCommittee) - .ToScriptHash(); - static readonly string MultisigAddress = MultisigScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); - - static readonly JArray validatorSigner = [new JObject() - { - ["account"] = ValidatorScriptHash.ToString(), - ["scopes"] = nameof(WitnessScope.CalledByEntry), - ["allowedcontracts"] = new JArray([NeoToken.NEO.Hash.ToString(), GasToken.GAS.Hash.ToString()]), - ["allowedgroups"] = new JArray([TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString()]), - ["rules"] = new JArray([new JObject() { ["action"] = nameof(WitnessRuleAction.Allow), ["condition"] = new JObject { ["type"] = nameof(WitnessConditionType.CalledByEntry) } }]), - }]; - static readonly JArray multisigSigner = [new JObject() + public partial class UT_RpcServer { - ["account"] = MultisigScriptHash.ToString(), - ["scopes"] = nameof(WitnessScope.CalledByEntry), - }]; - - [TestMethod] - public void TestInvokeFunction() - { - _rpcServer.wallet = _wallet; - - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "totalSupply", new JArray([]), validatorSigner, true)); - Assert.AreEqual(resp.Count, 8); - Assert.AreEqual(resp["script"], NeoTotalSupplyScript); - Assert.IsTrue(resp.ContainsProperty("gasconsumed")); - Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); - Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 0); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["exception"], null); - Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); - Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); - Assert.AreEqual(resp["stack"][0]["value"], "100000000"); - Assert.IsTrue(resp.ContainsProperty("tx")); - - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "symbol")); - Assert.AreEqual(resp.Count, 6); - Assert.IsTrue(resp.ContainsProperty("script")); - Assert.IsTrue(resp.ContainsProperty("gasconsumed")); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["exception"], null); - Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); - Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.ByteString)); - Assert.AreEqual(resp["stack"][0]["value"], Convert.ToBase64String(Encoding.UTF8.GetBytes("NEO"))); - - // This call triggers not only NEO but also unclaimed GAS - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "transfer", new JArray([ - new JObject() { ["type"] = nameof(ContractParameterType.Hash160), ["value"] = MultisigScriptHash.ToString() }, - new JObject() { ["type"] = nameof(ContractParameterType.Hash160), ["value"] = ValidatorScriptHash.ToString() }, - new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "1" }, - new JObject() { ["type"] = nameof(ContractParameterType.Any) }, - ]), multisigSigner, true)); - Assert.AreEqual(resp.Count, 7); - Assert.AreEqual(resp["script"], NeoTransferScript); - Assert.IsTrue(resp.ContainsProperty("gasconsumed")); - Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); - Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash} ({MultisigAddress}) is not found. " + - $"If this is your wallet address and you want to sign a transaction with it, make sure you have opened this wallet."); - JArray notifications = (JArray)resp["notifications"]; - Assert.AreEqual(notifications.Count, 2); - Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); - Assert.AreEqual(notifications[0]["contract"].AsString(), NeoToken.NEO.Hash.ToString()); - Assert.AreEqual(notifications[0]["state"]["value"][2]["value"], "1"); - Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); - Assert.AreEqual(notifications[1]["contract"].AsString(), GasToken.GAS.Hash.ToString()); - Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); - - _rpcServer.wallet = null; - } - - [TestMethod] - public void TestInvokeScript() - { - JObject resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTotalSupplyScript, validatorSigner, true)); - Assert.AreEqual(resp.Count, 7); - Assert.IsTrue(resp.ContainsProperty("gasconsumed")); - Assert.IsTrue(resp.ContainsProperty("diagnostics")); - Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["exception"], null); - Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); - Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); - Assert.AreEqual(resp["stack"][0]["value"], "100000000"); - - resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTransferScript)); - Assert.AreEqual(resp.Count, 6); - Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Boolean)); - Assert.AreEqual(resp["stack"][0]["value"], false); - } + static readonly string NeoTotalSupplyScript = "wh8MC3RvdGFsU3VwcGx5DBT1Y\u002BpAvCg9TQ4FxI6jBbPyoHNA70FifVtS"; + static readonly string NeoTransferScript = "CxEMFPlu76Cuc\u002BbgteStE4ozsOWTNUdrDBQtYNweHko3YcnMFOes3ceblcI/lRTAHwwIdHJhbnNmZXIMFPVj6kC8KD1NDgXEjqMFs/Kgc0DvQWJ9W1I="; + static readonly UInt160 ValidatorScriptHash = Contract + .CreateSignatureRedeemScript(TestProtocolSettings.SoleNode.StandbyCommittee[0]) + .ToScriptHash(); + static readonly string ValidatorAddress = ValidatorScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); + static readonly UInt160 MultisigScriptHash = Contract + .CreateMultiSigRedeemScript(1, TestProtocolSettings.SoleNode.StandbyCommittee) + .ToScriptHash(); + static readonly string MultisigAddress = MultisigScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); + + static readonly JArray validatorSigner = [new JObject() + { + ["account"] = ValidatorScriptHash.ToString(), + ["scopes"] = nameof(WitnessScope.CalledByEntry), + ["allowedcontracts"] = new JArray([NeoToken.NEO.Hash.ToString(), GasToken.GAS.Hash.ToString()]), + ["allowedgroups"] = new JArray([TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString()]), + ["rules"] = new JArray([new JObject() { ["action"] = nameof(WitnessRuleAction.Allow), ["condition"] = new JObject { ["type"] = nameof(WitnessConditionType.CalledByEntry) } }]), + }]; + static readonly JArray multisigSigner = [new JObject() + { + ["account"] = MultisigScriptHash.ToString(), + ["scopes"] = nameof(WitnessScope.CalledByEntry), + }]; - [TestMethod] - public void TestTraverseIterator() - { - // GetAllCandidates that should return 0 candidates - JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); - string sessionId = resp["session"].AsString(); - string iteratorId = resp["stack"][0]["id"].AsString(); - JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); - Assert.AreEqual(respArray.Count, 0); - _rpcServer.TerminateSession([sessionId]); - Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); - - // register candidate in snapshot - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "registerCandidate", - new JArray([new JObject() + [TestMethod] + public void TestInvokeFunction() + { + _rpcServer.wallet = _wallet; + + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "totalSupply", new JArray([]), validatorSigner, true)); + Assert.AreEqual(resp.Count, 8); + Assert.AreEqual(resp["script"], NeoTotalSupplyScript); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.IsTrue(resp.ContainsProperty("diagnostics")); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); + Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 0); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); + Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + Assert.IsTrue(resp.ContainsProperty("tx")); + + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "symbol")); + Assert.AreEqual(resp.Count, 6); + Assert.IsTrue(resp.ContainsProperty("script")); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.ByteString)); + Assert.AreEqual(resp["stack"][0]["value"], Convert.ToBase64String(Encoding.UTF8.GetBytes("NEO"))); + + // This call triggers not only NEO but also unclaimed GAS + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "transfer", new JArray([ + new JObject() { ["type"] = nameof(ContractParameterType.Hash160), ["value"] = MultisigScriptHash.ToString() }, + new JObject() { ["type"] = nameof(ContractParameterType.Hash160), ["value"] = ValidatorScriptHash.ToString() }, + new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "1" }, + new JObject() { ["type"] = nameof(ContractParameterType.Any) }, + ]), multisigSigner, true)); + Assert.AreEqual(resp.Count, 7); + Assert.AreEqual(resp["script"], NeoTransferScript); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.IsTrue(resp.ContainsProperty("diagnostics")); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); + Assert.IsTrue(((JArray)resp["diagnostics"]["storagechanges"]).Count == 4); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["exception"], $"The smart contract or address {MultisigScriptHash} ({MultisigAddress}) is not found. " + + $"If this is your wallet address and you want to sign a transaction with it, make sure you have opened this wallet."); + JArray notifications = (JArray)resp["notifications"]; + Assert.AreEqual(notifications.Count, 2); + Assert.AreEqual(notifications[0]["eventname"].AsString(), "Transfer"); + Assert.AreEqual(notifications[0]["contract"].AsString(), NeoToken.NEO.Hash.ToString()); + Assert.AreEqual(notifications[0]["state"]["value"][2]["value"], "1"); + Assert.AreEqual(notifications[1]["eventname"].AsString(), "Transfer"); + Assert.AreEqual(notifications[1]["contract"].AsString(), GasToken.GAS.Hash.ToString()); + Assert.AreEqual(notifications[1]["state"]["value"][2]["value"], "50000000"); + + _rpcServer.wallet = null; + } + + [TestMethod] + public void TestInvokeScript() + { + JObject resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTotalSupplyScript, validatorSigner, true)); + Assert.AreEqual(resp.Count, 7); + Assert.IsTrue(resp.ContainsProperty("gasconsumed")); + Assert.IsTrue(resp.ContainsProperty("diagnostics")); + Assert.AreEqual(resp["diagnostics"]["invokedcontracts"]["call"][0]["hash"], NeoToken.NEO.Hash.ToString()); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["exception"], null); + Assert.AreEqual(((JArray)resp["notifications"]).Count, 0); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Integer)); + Assert.AreEqual(resp["stack"][0]["value"], "100000000"); + + resp = (JObject)_rpcServer.InvokeScript(new JArray(NeoTransferScript)); + Assert.AreEqual(resp.Count, 6); + Assert.AreEqual(resp["stack"][0]["type"], nameof(Neo.VM.Types.Boolean)); + Assert.AreEqual(resp["stack"][0]["value"], false); + } + + [TestMethod] + public void TestTraverseIterator() + { + // GetAllCandidates that should return 0 candidates + JObject resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + string sessionId = resp["session"].AsString(); + string iteratorId = resp["stack"][0]["id"].AsString(); + JArray respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + Assert.AreEqual(respArray.Count, 0); + _rpcServer.TerminateSession([sessionId]); + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); + + // register candidate in snapshot + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "registerCandidate", + new JArray([new JObject() + { + ["type"] = nameof(ContractParameterType.PublicKey), + ["value"] = TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString(), + }]), validatorSigner, true)); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); + Transaction? tx = new Transaction { - ["type"] = nameof(ContractParameterType.PublicKey), - ["value"] = TestProtocolSettings.SoleNode.StandbyCommittee[0].ToString(), - }]), validatorSigner, true)); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); - Transaction? tx = new Transaction + Nonce = 233, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + _neoSystem.Settings.MaxValidUntilBlockIncrement, + Signers = [new Signer() { Account = ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], + Attributes = Array.Empty(), + Script = Convert.FromBase64String(resp["script"].AsString()), + Witnesses = null, + }; + ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: _neoSystem.Settings, gas: 1200_0000_0000); + engine.SnapshotCache.Commit(); + + // GetAllCandidates that should return 1 candidate + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + sessionId = resp["session"].AsString(); + iteratorId = resp["stack"][0]["id"].AsString(); + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + Assert.AreEqual(respArray.Count, 1); + Assert.AreEqual(respArray[0]["type"], nameof(Neo.VM.Types.Struct)); + JArray value = (JArray)respArray[0]["value"]; + Assert.AreEqual(value.Count, 2); + Assert.AreEqual(value[0]["type"], nameof(Neo.VM.Types.ByteString)); + Assert.AreEqual(value[0]["value"], Convert.ToBase64String(TestProtocolSettings.SoleNode.StandbyCommittee[0].ToArray())); + Assert.AreEqual(value[1]["type"], nameof(Neo.VM.Types.Integer)); + Assert.AreEqual(value[1]["value"], "0"); + + // No result when traversed again + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); + Assert.AreEqual(respArray.Count, 0); + + // GetAllCandidates again + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + sessionId = resp["session"].AsString(); + iteratorId = resp["stack"][0]["id"].AsString(); + + // Insufficient result count limit + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 0]); + Assert.AreEqual(respArray.Count, 0); + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 1]); + Assert.AreEqual(respArray.Count, 1); + respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 1]); + Assert.AreEqual(respArray.Count, 0); + + // Mocking session timeout + Thread.Sleep((int)_rpcServerSettings.SessionExpirationTime.TotalMilliseconds + 1); + // build another session that did not expire + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + string notExpiredSessionId = resp["session"].AsString(); + string notExpiredIteratorId = resp["stack"][0]["id"].AsString(); + _rpcServer.OnTimer(new object()); + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); + // If you want to run the following line without exception, + // DO NOT BREAK IN THE DEBUGGER, because the session expires quickly + respArray = (JArray)_rpcServer.TraverseIterator([notExpiredSessionId, notExpiredIteratorId, 1]); + Assert.AreEqual(respArray.Count, 1); + + // Mocking disposal + resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); + sessionId = resp["session"].AsString(); + iteratorId = resp["stack"][0]["id"].AsString(); + _rpcServer.Dispose_SmartContract(); + Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); + } + + [TestMethod] + public void TestGetUnclaimedGas() { - Nonce = 233, - ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + _neoSystem.Settings.MaxValidUntilBlockIncrement, - Signers = [new Signer() { Account = ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], - Attributes = Array.Empty(), - Script = Convert.FromBase64String(resp["script"].AsString()), - Witnesses = null, - }; - ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: _neoSystem.Settings, gas: 1200_0000_0000); - engine.SnapshotCache.Commit(); - - // GetAllCandidates that should return 1 candidate - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); - sessionId = resp["session"].AsString(); - iteratorId = resp["stack"][0]["id"].AsString(); - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); - Assert.AreEqual(respArray.Count, 1); - Assert.AreEqual(respArray[0]["type"], nameof(Neo.VM.Types.Struct)); - JArray value = (JArray)respArray[0]["value"]; - Assert.AreEqual(value.Count, 2); - Assert.AreEqual(value[0]["type"], nameof(Neo.VM.Types.ByteString)); - Assert.AreEqual(value[0]["value"], Convert.ToBase64String(TestProtocolSettings.SoleNode.StandbyCommittee[0].ToArray())); - Assert.AreEqual(value[1]["type"], nameof(Neo.VM.Types.Integer)); - Assert.AreEqual(value[1]["value"], "0"); - - // No result when traversed again - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]); - Assert.AreEqual(respArray.Count, 0); - - // GetAllCandidates again - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); - sessionId = resp["session"].AsString(); - iteratorId = resp["stack"][0]["id"].AsString(); - - // Insufficient result count limit - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 0]); - Assert.AreEqual(respArray.Count, 0); - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 1]); - Assert.AreEqual(respArray.Count, 1); - respArray = (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 1]); - Assert.AreEqual(respArray.Count, 0); - - // Mocking session timeout - Thread.Sleep((int)_rpcServerSettings.SessionExpirationTime.TotalMilliseconds + 1); - // build another session that did not expire - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); - string notExpiredSessionId = resp["session"].AsString(); - string notExpiredIteratorId = resp["stack"][0]["id"].AsString(); - _rpcServer.OnTimer(new object()); - Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); - // If you want to run the following line without exception, - // DO NOT BREAK IN THE DEBUGGER, because the session expires quickly - respArray = (JArray)_rpcServer.TraverseIterator([notExpiredSessionId, notExpiredIteratorId, 1]); - Assert.AreEqual(respArray.Count, 1); - - // Mocking disposal - resp = (JObject)_rpcServer.InvokeFunction(new JArray(NeoToken.NEO.Hash.ToString(), "getAllCandidates", new JArray([]), validatorSigner, true)); - sessionId = resp["session"].AsString(); - iteratorId = resp["stack"][0]["id"].AsString(); - _rpcServer.Dispose_SmartContract(); - Assert.ThrowsException(() => (JArray)_rpcServer.TraverseIterator([sessionId, iteratorId, 100]), "Unknown session"); - } - - [TestMethod] - public void TestGetUnclaimedGas() - { - JObject resp = (JObject)_rpcServer.GetUnclaimedGas([MultisigAddress]); - Assert.AreEqual(resp["unclaimed"], "50000000"); - Assert.AreEqual(resp["address"], MultisigAddress); - resp = (JObject)_rpcServer.GetUnclaimedGas([ValidatorAddress]); - Assert.AreEqual(resp["unclaimed"], "0"); - Assert.AreEqual(resp["address"], ValidatorAddress); + JObject resp = (JObject)_rpcServer.GetUnclaimedGas([MultisigAddress]); + Assert.AreEqual(resp["unclaimed"], "50000000"); + Assert.AreEqual(resp["address"], MultisigAddress); + resp = (JObject)_rpcServer.GetUnclaimedGas([ValidatorAddress]); + Assert.AreEqual(resp["unclaimed"], "0"); + Assert.AreEqual(resp["address"], ValidatorAddress); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs index a0f2384ce5..b862d0c97a 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Utilities.cs @@ -23,32 +23,33 @@ using System.IO; using System.Linq; -namespace Neo.Plugins.RpcServer.Tests; - -public partial class UT_RpcServer +namespace Neo.Plugins.RpcServer.Tests { - [TestMethod] - public void TestListPlugins() + public partial class UT_RpcServer { - JArray resp = (JArray)_rpcServer.ListPlugins([]); - Assert.AreEqual(resp.Count, 0); - Plugins.Plugin.Plugins.Add(new RpcServerPlugin()); - resp = (JArray)_rpcServer.ListPlugins([]); - Assert.AreEqual(resp.Count, 2); - foreach (JObject p in resp) - Assert.AreEqual(p["name"], nameof(RpcServer)); - } + [TestMethod] + public void TestListPlugins() + { + JArray resp = (JArray)_rpcServer.ListPlugins([]); + Assert.AreEqual(resp.Count, 0); + Plugins.Plugin.Plugins.Add(new RpcServerPlugin()); + resp = (JArray)_rpcServer.ListPlugins([]); + Assert.AreEqual(resp.Count, 2); + foreach (JObject p in resp) + Assert.AreEqual(p["name"], nameof(RpcServer)); + } - [TestMethod] - public void TestValidateAddress() - { - string validAddr = "NM7Aky765FG8NhhwtxjXRx7jEL1cnw7PBP"; - JObject resp = (JObject)_rpcServer.ValidateAddress([validAddr]); - Assert.AreEqual(resp["address"], validAddr); - Assert.AreEqual(resp["isvalid"], true); - string invalidAddr = "ANeo2toNeo3MigrationAddressxwPB2Hz"; - resp = (JObject)_rpcServer.ValidateAddress([invalidAddr]); - Assert.AreEqual(resp["address"], invalidAddr); - Assert.AreEqual(resp["isvalid"], false); + [TestMethod] + public void TestValidateAddress() + { + string validAddr = "NM7Aky765FG8NhhwtxjXRx7jEL1cnw7PBP"; + JObject resp = (JObject)_rpcServer.ValidateAddress([validAddr]); + Assert.AreEqual(resp["address"], validAddr); + Assert.AreEqual(resp["isvalid"], true); + string invalidAddr = "ANeo2toNeo3MigrationAddressxwPB2Hz"; + resp = (JObject)_rpcServer.ValidateAddress([invalidAddr]); + Assert.AreEqual(resp["address"], invalidAddr); + Assert.AreEqual(resp["isvalid"], false); + } } } diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 0897a12804..ad35975352 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; @@ -24,377 +25,377 @@ using System.IO; using System.Linq; -namespace Neo.Plugins.RpcServer.Tests; - -partial class UT_RpcServer +namespace Neo.Plugins.RpcServer.Tests { - [TestMethod] - public void TestOpenWallet() + partial class UT_RpcServer { - const string Path = "wallet.json"; - const string Password = "123456"; - File.WriteAllText(Path, "{\"name\":null,\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[{\"address\":\"NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv\",\"label\":null,\"isDefault\":false,\"lock\":false,\"key\":\"6PYPMrsCJ3D4AXJCFWYT2WMSBGF7dLoaNipW14t4UFAkZw3Z9vQRQV1bEU\",\"contract\":{\"script\":\"DCEDaR\\u002BFVb8lOdiMZ/wCHLiI\\u002Bzuf17YuGFReFyHQhB80yMpBVuezJw==\",\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false},\"extra\":null}],\"extra\":null}"); - var paramsArray = new JArray(Path, Password); - var res = _rpcServer.OpenWallet(paramsArray); - Assert.IsTrue(res.AsBoolean()); - Assert.IsNotNull(_rpcServer.wallet); - Assert.AreEqual(_rpcServer.wallet.GetAccounts().FirstOrDefault()!.Address, "NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv"); - _rpcServer.CloseWallet([]); - File.Delete(Path); - Assert.IsNull(_rpcServer.wallet); - } + [TestMethod] + public void TestOpenWallet() + { + const string Path = "wallet.json"; + const string Password = "123456"; + File.WriteAllText(Path, "{\"name\":null,\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[{\"address\":\"NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv\",\"label\":null,\"isDefault\":false,\"lock\":false,\"key\":\"6PYPMrsCJ3D4AXJCFWYT2WMSBGF7dLoaNipW14t4UFAkZw3Z9vQRQV1bEU\",\"contract\":{\"script\":\"DCEDaR\\u002BFVb8lOdiMZ/wCHLiI\\u002Bzuf17YuGFReFyHQhB80yMpBVuezJw==\",\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false},\"extra\":null}],\"extra\":null}"); + var paramsArray = new JArray(Path, Password); + var res = _rpcServer.OpenWallet(paramsArray); + Assert.IsTrue(res.AsBoolean()); + Assert.IsNotNull(_rpcServer.wallet); + Assert.AreEqual(_rpcServer.wallet.GetAccounts().FirstOrDefault()!.Address, "NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv"); + _rpcServer.CloseWallet([]); + File.Delete(Path); + Assert.IsNull(_rpcServer.wallet); + } - [TestMethod] - public void TestOpenInvalidWallet() - { - const string Path = "wallet.json"; - const string Password = "password"; - File.Delete(Path); - var paramsArray = new JArray(Path, Password); - var exception = Assert.ThrowsException(() => _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); - Assert.AreEqual(RpcError.WalletNotFound.Code, exception.HResult); - - File.WriteAllText(Path, "{}"); - exception = Assert.ThrowsException(() => _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); - File.Delete(Path); - Assert.AreEqual(RpcError.WalletNotSupported.Code, exception.HResult); - var result = _rpcServer.CloseWallet(new JArray()); - Assert.IsTrue(result.AsBoolean()); - Assert.IsNull(_rpcServer.wallet); - - File.WriteAllText(Path, "{\"name\":null,\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[{\"address\":\"NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv\",\"label\":null,\"isDefault\":false,\"lock\":false,\"key\":\"6PYPMrsCJ3D4AXJCFWYT2WMSBGF7dLoaNipW14t4UFAkZw3Z9vQRQV1bEU\",\"contract\":{\"script\":\"DCEDaR\\u002BFVb8lOdiMZ/wCHLiI\\u002Bzuf17YuGFReFyHQhB80yMpBVuezJw==\",\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false},\"extra\":null}],\"extra\":null}"); - exception = Assert.ThrowsException(() => _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); - Assert.AreEqual(RpcError.WalletNotSupported.Code, exception.HResult); - Assert.AreEqual(exception.Message, "Wallet not supported - Invalid password."); - File.Delete(Path); - } + [TestMethod] + public void TestOpenInvalidWallet() + { + const string Path = "wallet.json"; + const string Password = "password"; + File.Delete(Path); + var paramsArray = new JArray(Path, Password); + var exception = Assert.ThrowsException(() => _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); + Assert.AreEqual(RpcError.WalletNotFound.Code, exception.HResult); - [TestMethod] - public void TestDumpPrivKey() - { - TestUtilOpenWallet(); - var account = _rpcServer.wallet.GetAccounts().FirstOrDefault(); - Assert.IsNotNull(account); - var privKey = account.GetKey().Export(); - var address = account.Address; - var result = _rpcServer.DumpPrivKey(new JArray(address)); - Assert.AreEqual(privKey, result.AsString()); - TestUtilCloseWallet(); - } + File.WriteAllText(Path, "{}"); + exception = Assert.ThrowsException(() => _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); + File.Delete(Path); + Assert.AreEqual(RpcError.WalletNotSupported.Code, exception.HResult); + var result = _rpcServer.CloseWallet(new JArray()); + Assert.IsTrue(result.AsBoolean()); + Assert.IsNull(_rpcServer.wallet); - [TestMethod] - public void TestGetNewAddress() - { - TestUtilOpenWallet(); - var result = _rpcServer.GetNewAddress([]); - Assert.IsInstanceOfType(result, typeof(JString)); - Assert.IsTrue(_rpcServer.wallet.GetAccounts().Any(a => a.Address == result.AsString())); - TestUtilCloseWallet(); - } + File.WriteAllText(Path, "{\"name\":null,\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[{\"address\":\"NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv\",\"label\":null,\"isDefault\":false,\"lock\":false,\"key\":\"6PYPMrsCJ3D4AXJCFWYT2WMSBGF7dLoaNipW14t4UFAkZw3Z9vQRQV1bEU\",\"contract\":{\"script\":\"DCEDaR\\u002BFVb8lOdiMZ/wCHLiI\\u002Bzuf17YuGFReFyHQhB80yMpBVuezJw==\",\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false},\"extra\":null}],\"extra\":null}"); + exception = Assert.ThrowsException(() => _rpcServer.OpenWallet(paramsArray), "Should throw RpcException for unsupported wallet"); + Assert.AreEqual(RpcError.WalletNotSupported.Code, exception.HResult); + Assert.AreEqual(exception.Message, "Wallet not supported - Invalid password."); + File.Delete(Path); + } - [TestMethod] - public void TestGetWalletBalance() - { - TestUtilOpenWallet(); - var assetId = NativeContract.NEO.Hash; - var paramsArray = new JArray(assetId.ToString()); - var result = _rpcServer.GetWalletBalance(paramsArray); - Assert.IsInstanceOfType(result, typeof(JObject)); - var json = (JObject)result; - Assert.IsTrue(json.ContainsProperty("balance")); - TestUtilCloseWallet(); - } + [TestMethod] + public void TestDumpPrivKey() + { + TestUtilOpenWallet(); + var account = _rpcServer.wallet.GetAccounts().FirstOrDefault(); + Assert.IsNotNull(account); + var privKey = account.GetKey().Export(); + var address = account.Address; + var result = _rpcServer.DumpPrivKey(new JArray(address)); + Assert.AreEqual(privKey, result.AsString()); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestGetWalletBalanceInvalidAsset() - { - TestUtilOpenWallet(); - var assetId = UInt160.Zero; - var paramsArray = new JArray(assetId.ToString()); - var result = _rpcServer.GetWalletBalance(paramsArray); - Assert.IsInstanceOfType(result, typeof(JObject)); - var json = (JObject)result; - Assert.IsTrue(json.ContainsProperty("balance")); - TestUtilCloseWallet(); - } + [TestMethod] + public void TestGetNewAddress() + { + TestUtilOpenWallet(); + var result = _rpcServer.GetNewAddress([]); + Assert.IsInstanceOfType(result, typeof(JString)); + Assert.IsTrue(_rpcServer.wallet.GetAccounts().Any(a => a.Address == result.AsString())); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestGetWalletUnclaimedGas() - { - TestUtilOpenWallet(); - var result = _rpcServer.GetWalletUnclaimedGas([]); - Assert.IsInstanceOfType(result, typeof(JString)); - TestUtilCloseWallet(); - } + [TestMethod] + public void TestGetWalletBalance() + { + TestUtilOpenWallet(); + var assetId = NativeContract.NEO.Hash; + var paramsArray = new JArray(assetId.ToString()); + var result = _rpcServer.GetWalletBalance(paramsArray); + Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; + Assert.IsTrue(json.ContainsProperty("balance")); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestImportPrivKey() - { - TestUtilOpenWallet(); - var privKey = _walletAccount.GetKey().Export(); - var paramsArray = new JArray(privKey); - var result = _rpcServer.ImportPrivKey(paramsArray); - Assert.IsInstanceOfType(result, typeof(JObject)); - var json = (JObject)result; - Assert.IsTrue(json.ContainsProperty("address")); - Assert.IsTrue(json.ContainsProperty("haskey")); - Assert.IsTrue(json.ContainsProperty("label")); - Assert.IsTrue(json.ContainsProperty("watchonly")); - TestUtilCloseWallet(); - } + [TestMethod] + public void TestGetWalletBalanceInvalidAsset() + { + TestUtilOpenWallet(); + var assetId = UInt160.Zero; + var paramsArray = new JArray(assetId.ToString()); + var result = _rpcServer.GetWalletBalance(paramsArray); + Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; + Assert.IsTrue(json.ContainsProperty("balance")); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestImportPrivKeyNoWallet() - { - var privKey = _walletAccount.GetKey().Export(); - var paramsArray = new JArray(privKey); - var exception = Assert.ThrowsException(() => _rpcServer.ImportPrivKey(paramsArray)); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestGetWalletUnclaimedGas() + { + TestUtilOpenWallet(); + var result = _rpcServer.GetWalletUnclaimedGas([]); + Assert.IsInstanceOfType(result, typeof(JString)); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestCalculateNetworkFee() - { - var snapshot = _neoSystem.GetSnapshot(); - var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); - var txBase64 = Convert.ToBase64String(tx.ToArray()); - var paramsArray = new JArray(txBase64); - var result = _rpcServer.CalculateNetworkFee(paramsArray); - Assert.IsInstanceOfType(result, typeof(JObject)); - var json = (JObject)result; - Assert.IsTrue(json.ContainsProperty("networkfee")); - } + [TestMethod] + public void TestImportPrivKey() + { + TestUtilOpenWallet(); + var privKey = _walletAccount.GetKey().Export(); + var paramsArray = new JArray(privKey); + var result = _rpcServer.ImportPrivKey(paramsArray); + Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; + Assert.IsTrue(json.ContainsProperty("address")); + Assert.IsTrue(json.ContainsProperty("haskey")); + Assert.IsTrue(json.ContainsProperty("label")); + Assert.IsTrue(json.ContainsProperty("watchonly")); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestCalculateNetworkFeeNoParam() - { - var exception = Assert.ThrowsException(() => _rpcServer.CalculateNetworkFee([])); - Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); - } + [TestMethod] + public void TestImportPrivKeyNoWallet() + { + var privKey = _walletAccount.GetKey().Export(); + var paramsArray = new JArray(privKey); + var exception = Assert.ThrowsException(() => _rpcServer.ImportPrivKey(paramsArray)); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestListAddressNoWallet() - { - var exception = Assert.ThrowsException(() => _rpcServer.ListAddress([])); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestCalculateNetworkFee() + { + var snapshot = _neoSystem.GetSnapshot(); + var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); + var txBase64 = Convert.ToBase64String(tx.ToArray()); + var paramsArray = new JArray(txBase64); + var result = _rpcServer.CalculateNetworkFee(paramsArray); + Assert.IsInstanceOfType(result, typeof(JObject)); + var json = (JObject)result; + Assert.IsTrue(json.ContainsProperty("networkfee")); + } - [TestMethod] - public void TestListAddress() - { - TestUtilOpenWallet(); - var result = _rpcServer.ListAddress([]); - Assert.IsInstanceOfType(result, typeof(JArray)); - var json = (JArray)result; - Assert.IsTrue(json.Count > 0); - TestUtilCloseWallet(); - } + [TestMethod] + public void TestCalculateNetworkFeeNoParam() + { + var exception = Assert.ThrowsException(() => _rpcServer.CalculateNetworkFee([])); + Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); + } - [TestMethod] - public void TestSendFromNoWallet() - { - var assetId = NativeContract.GAS.Hash; - var from = _walletAccount.Address; - var to = _walletAccount.Address; - var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), from, to, amount); - var exception = Assert.ThrowsException(() => _rpcServer.SendFrom(paramsArray), "Should throw RpcException for insufficient funds"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestListAddressNoWallet() + { + var exception = Assert.ThrowsException(() => _rpcServer.ListAddress([])); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestSendFrom() - { - TestUtilOpenWallet(); - var assetId = NativeContract.GAS.Hash; - var from = _walletAccount.Address; - var to = _walletAccount.Address; - var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), from, to, amount); - var exception = Assert.ThrowsException(() => _rpcServer.SendFrom(paramsArray)); - Assert.AreEqual(exception.HResult, RpcError.InvalidRequest.Code); - TestUtilCloseWallet(); - - _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendFrom(paramsArray); - Assert.AreEqual(resp.Count, 12); - Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; - Assert.AreEqual(signers.Count, 1); - Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); - _rpcServer.wallet = null; - } + [TestMethod] + public void TestListAddress() + { + TestUtilOpenWallet(); + var result = _rpcServer.ListAddress([]); + Assert.IsInstanceOfType(result, typeof(JArray)); + var json = (JArray)result; + Assert.IsTrue(json.Count > 0); + TestUtilCloseWallet(); + } - [TestMethod] - public void TestSendMany() - { - var from = _walletAccount.Address; - var to = new JArray { new JObject { ["asset"] = NativeContract.GAS.Hash.ToString(), ["value"] = "1", ["address"] = _walletAccount.Address } }; - var paramsArray = new JArray(from, to); - var exception = Assert.ThrowsException(() => _rpcServer.SendMany(paramsArray), "Should throw RpcException for insufficient funds"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - - _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendMany(paramsArray); - Assert.AreEqual(resp.Count, 12); - Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; - Assert.AreEqual(signers.Count, 1); - Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); - _rpcServer.wallet = null; - } + [TestMethod] + public void TestSendFromNoWallet() + { + var assetId = NativeContract.GAS.Hash; + var from = _walletAccount.Address; + var to = _walletAccount.Address; + var amount = "1"; + var paramsArray = new JArray(assetId.ToString(), from, to, amount); + var exception = Assert.ThrowsException(() => _rpcServer.SendFrom(paramsArray), "Should throw RpcException for insufficient funds"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestSendToAddress() - { - var assetId = NativeContract.GAS.Hash; - var to = _walletAccount.Address; - var amount = "1"; - var paramsArray = new JArray(assetId.ToString(), to, amount); - var exception = Assert.ThrowsException(() => _rpcServer.SendToAddress(paramsArray), "Should throw RpcException for insufficient funds"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - - _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendToAddress(paramsArray); - Assert.AreEqual(resp.Count, 12); - Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; - Assert.AreEqual(signers.Count, 1); - Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); - _rpcServer.wallet = null; - } + [TestMethod] + public void TestSendFrom() + { + TestUtilOpenWallet(); + var assetId = NativeContract.GAS.Hash; + var from = _walletAccount.Address; + var to = _walletAccount.Address; + var amount = "1"; + var paramsArray = new JArray(assetId.ToString(), from, to, amount); + var exception = Assert.ThrowsException(() => _rpcServer.SendFrom(paramsArray)); + Assert.AreEqual(exception.HResult, RpcError.InvalidRequest.Code); + TestUtilCloseWallet(); + + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendFrom(paramsArray); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); + _rpcServer.wallet = null; + } - [TestMethod] - public void TestCloseWallet_WhenWalletNotOpen() - { - _rpcServer.wallet = null; - var result = _rpcServer.CloseWallet(new JArray()); - Assert.IsTrue(result.AsBoolean()); - } + [TestMethod] + public void TestSendMany() + { + var from = _walletAccount.Address; + var to = new JArray { new JObject { ["asset"] = NativeContract.GAS.Hash.ToString(), ["value"] = "1", ["address"] = _walletAccount.Address } }; + var paramsArray = new JArray(from, to); + var exception = Assert.ThrowsException(() => _rpcServer.SendMany(paramsArray), "Should throw RpcException for insufficient funds"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendMany(paramsArray); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); + _rpcServer.wallet = null; + } - [TestMethod] - public void TestDumpPrivKey_WhenWalletNotOpen() - { - _rpcServer.wallet = null; - var exception = Assert.ThrowsException(() => _rpcServer.DumpPrivKey(new JArray(_walletAccount.Address)), "Should throw RpcException for no opened wallet"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestSendToAddress() + { + var assetId = NativeContract.GAS.Hash; + var to = _walletAccount.Address; + var amount = "1"; + var paramsArray = new JArray(assetId.ToString(), to, amount); + var exception = Assert.ThrowsException(() => _rpcServer.SendToAddress(paramsArray), "Should throw RpcException for insufficient funds"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendToAddress(paramsArray); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.CalledByEntry)); + _rpcServer.wallet = null; + } - [TestMethod] - public void TestGetNewAddress_WhenWalletNotOpen() - { - _rpcServer.wallet = null; - var exception = Assert.ThrowsException(() => _rpcServer.GetNewAddress(new JArray()), "Should throw RpcException for no opened wallet"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestCloseWallet_WhenWalletNotOpen() + { + _rpcServer.wallet = null; + var result = _rpcServer.CloseWallet(new JArray()); + Assert.IsTrue(result.AsBoolean()); + } - [TestMethod] - public void TestGetWalletBalance_WhenWalletNotOpen() - { - _rpcServer.wallet = null; - var exception = Assert.ThrowsException(() => _rpcServer.GetWalletBalance(new JArray(NativeContract.NEO.Hash.ToString())), "Should throw RpcException for no opened wallet"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestDumpPrivKey_WhenWalletNotOpen() + { + _rpcServer.wallet = null; + var exception = Assert.ThrowsException(() => _rpcServer.DumpPrivKey(new JArray(_walletAccount.Address)), "Should throw RpcException for no opened wallet"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestGetWalletUnclaimedGas_WhenWalletNotOpen() - { - _rpcServer.wallet = null; - var exception = Assert.ThrowsException(() => _rpcServer.GetWalletUnclaimedGas(new JArray()), "Should throw RpcException for no opened wallet"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestGetNewAddress_WhenWalletNotOpen() + { + _rpcServer.wallet = null; + var exception = Assert.ThrowsException(() => _rpcServer.GetNewAddress(new JArray()), "Should throw RpcException for no opened wallet"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestImportPrivKey_WhenWalletNotOpen() - { - _rpcServer.wallet = null; - var privKey = _walletAccount.GetKey().Export(); - var exception = Assert.ThrowsException(() => _rpcServer.ImportPrivKey(new JArray(privKey)), "Should throw RpcException for no opened wallet"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - } + [TestMethod] + public void TestGetWalletBalance_WhenWalletNotOpen() + { + _rpcServer.wallet = null; + var exception = Assert.ThrowsException(() => _rpcServer.GetWalletBalance(new JArray(NativeContract.NEO.Hash.ToString())), "Should throw RpcException for no opened wallet"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestCalculateNetworkFee_InvalidTransactionFormat() - { - var invalidTxBase64 = "invalid_base64"; - var paramsArray = new JArray(invalidTxBase64); - var exception = Assert.ThrowsException(() => _rpcServer.CalculateNetworkFee(paramsArray), "Should throw RpcException for invalid transaction format"); - Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); - } + [TestMethod] + public void TestGetWalletUnclaimedGas_WhenWalletNotOpen() + { + _rpcServer.wallet = null; + var exception = Assert.ThrowsException(() => _rpcServer.GetWalletUnclaimedGas(new JArray()), "Should throw RpcException for no opened wallet"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - [TestMethod] - public void TestListAddress_WhenWalletNotOpen() - { - // Ensure the wallet is not open - _rpcServer.wallet = null; + [TestMethod] + public void TestImportPrivKey_WhenWalletNotOpen() + { + _rpcServer.wallet = null; + var privKey = _walletAccount.GetKey().Export(); + var exception = Assert.ThrowsException(() => _rpcServer.ImportPrivKey(new JArray(privKey)), "Should throw RpcException for no opened wallet"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + } - // Attempt to call ListAddress and expect an RpcException - var exception = Assert.ThrowsException(() => _rpcServer.ListAddress(new JArray())); + [TestMethod] + public void TestCalculateNetworkFee_InvalidTransactionFormat() + { + var invalidTxBase64 = "invalid_base64"; + var paramsArray = new JArray(invalidTxBase64); + var exception = Assert.ThrowsException(() => _rpcServer.CalculateNetworkFee(paramsArray), "Should throw RpcException for invalid transaction format"); + Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); + } - // Verify the exception has the expected error code - Assert.AreEqual(RpcError.NoOpenedWallet.Code, exception.HResult); - } + [TestMethod] + public void TestListAddress_WhenWalletNotOpen() + { + // Ensure the wallet is not open + _rpcServer.wallet = null; - [TestMethod] - public void TestCancelTransaction() - { - TestUtilOpenWallet(); - var snapshot = _neoSystem.GetSnapshot(); - var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); - snapshot.Commit(); - var paramsArray = new JArray(tx.Hash.ToString(), new JArray(_walletAccount.Address)); - var exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(paramsArray), "Should throw RpcException for non-existing transaction"); - - Assert.AreEqual(RpcError.InsufficientFunds.Code, exception.HResult); - - // Test with invalid transaction id - var invalidParamsArray = new JArray("invalid_txid", new JArray(_walletAccount.Address)); - exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(invalidParamsArray), "Should throw RpcException for invalid txid"); - Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); - - // Test with no signer - invalidParamsArray = new JArray(tx.Hash.ToString()); - exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(invalidParamsArray), "Should throw RpcException for invalid txid"); - Assert.AreEqual(exception.HResult, RpcError.BadRequest.Code); - - // Test with null wallet - _rpcServer.wallet = null; - exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(paramsArray), "Should throw RpcException for no opened wallet"); - Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); - TestUtilCloseWallet(); - - // Test valid cancel - _rpcServer.wallet = _wallet; - JObject resp = (JObject)_rpcServer.SendFrom(new JArray(NativeContract.GAS.Hash.ToString(), _walletAccount.Address, _walletAccount.Address, "1")); - string txHash = resp["hash"].AsString(); - resp = (JObject)_rpcServer.CancelTransaction(new JArray(txHash, new JArray(ValidatorAddress), "1")); - Assert.AreEqual(resp.Count, 12); - Assert.AreEqual(resp["sender"], ValidatorAddress); - JArray signers = (JArray)resp["signers"]; - Assert.AreEqual(signers.Count, 1); - Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); - Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.None)); - Assert.AreEqual(resp["attributes"][0]["type"], nameof(TransactionAttributeType.Conflicts)); - _rpcServer.wallet = null; - } + // Attempt to call ListAddress and expect an RpcException + var exception = Assert.ThrowsException(() => _rpcServer.ListAddress(new JArray())); - [TestMethod] - public void TestInvokeContractVerify() - { - var scriptHash = UInt160.Parse("0x70cde1619e405cdef363ab66a1e8dce430d798d5"); - var paramsArray = new JArray(scriptHash.ToString()); - var exception = Assert.ThrowsException(() => _rpcServer.InvokeContractVerify(paramsArray), "Should throw RpcException for unknown contract"); - Assert.AreEqual(exception.HResult, RpcError.UnknownContract.Code); - // Test with invalid script hash - var invalidParamsArray = new JArray("invalid_script_hash"); - exception = Assert.ThrowsException(() => _rpcServer.InvokeContractVerify(invalidParamsArray), "Should throw RpcException for invalid script hash"); - Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); - - // deploy a contract with `Verify` method; - string _contractSourceCode = """ + // Verify the exception has the expected error code + Assert.AreEqual(RpcError.NoOpenedWallet.Code, exception.HResult); + } + + [TestMethod] + public void TestCancelTransaction() + { + TestUtilOpenWallet(); + var snapshot = _neoSystem.GetSnapshot(); + var tx = TestUtils.CreateValidTx(snapshot, _wallet, _walletAccount); + snapshot.Commit(); + var paramsArray = new JArray(tx.Hash.ToString(), new JArray(_walletAccount.Address)); + var exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(paramsArray), "Should throw RpcException for non-existing transaction"); + + Assert.AreEqual(RpcError.InsufficientFunds.Code, exception.HResult); + + // Test with invalid transaction id + var invalidParamsArray = new JArray("invalid_txid", new JArray(_walletAccount.Address)); + exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(invalidParamsArray), "Should throw RpcException for invalid txid"); + Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); + + // Test with no signer + invalidParamsArray = new JArray(tx.Hash.ToString()); + exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(invalidParamsArray), "Should throw RpcException for invalid txid"); + Assert.AreEqual(exception.HResult, RpcError.BadRequest.Code); + + // Test with null wallet + _rpcServer.wallet = null; + exception = Assert.ThrowsException(() => _rpcServer.CancelTransaction(paramsArray), "Should throw RpcException for no opened wallet"); + Assert.AreEqual(exception.HResult, RpcError.NoOpenedWallet.Code); + TestUtilCloseWallet(); + + // Test valid cancel + _rpcServer.wallet = _wallet; + JObject resp = (JObject)_rpcServer.SendFrom(new JArray(NativeContract.GAS.Hash.ToString(), _walletAccount.Address, _walletAccount.Address, "1")); + string txHash = resp["hash"].AsString(); + resp = (JObject)_rpcServer.CancelTransaction(new JArray(txHash, new JArray(ValidatorAddress), "1")); + Assert.AreEqual(resp.Count, 12); + Assert.AreEqual(resp["sender"], ValidatorAddress); + JArray signers = (JArray)resp["signers"]; + Assert.AreEqual(signers.Count, 1); + Assert.AreEqual(signers[0]["account"], ValidatorScriptHash.ToString()); + Assert.AreEqual(signers[0]["scopes"], nameof(WitnessScope.None)); + Assert.AreEqual(resp["attributes"][0]["type"], nameof(TransactionAttributeType.Conflicts)); + _rpcServer.wallet = null; + } + + [TestMethod] + public void TestInvokeContractVerify() + { + var scriptHash = UInt160.Parse("0x70cde1619e405cdef363ab66a1e8dce430d798d5"); + var paramsArray = new JArray(scriptHash.ToString()); + var exception = Assert.ThrowsException(() => _rpcServer.InvokeContractVerify(paramsArray), "Should throw RpcException for unknown contract"); + Assert.AreEqual(exception.HResult, RpcError.UnknownContract.Code); + // Test with invalid script hash + var invalidParamsArray = new JArray("invalid_script_hash"); + exception = Assert.ThrowsException(() => _rpcServer.InvokeContractVerify(invalidParamsArray), "Should throw RpcException for invalid script hash"); + Assert.AreEqual(exception.HResult, RpcError.InvalidParams.Code); + + // deploy a contract with `Verify` method; + string _contractSourceCode = """ using Neo;using Neo.SmartContract.Framework;using Neo.SmartContract.Framework.Services; namespace ContractWithVerify{public class ContractWithVerify:SmartContract { const byte PREFIX_OWNER = 0x20; @@ -405,101 +406,102 @@ public static void _deploy(object data, bool update) { public static bool Verify() => Runtime.CheckWitness((UInt160)Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_OWNER })); public static bool Verify(byte prefix) => Runtime.CheckWitness((UInt160)Storage.Get(Storage.CurrentContext, new byte[] { prefix }));}} """; - string base64NefFile = "TkVGM05lby5Db21waWxlci5DU2hhcnAgMy43LjQrNjAzNGExODIxY2E3MDk0NjBlYzMxMzZjNzBjMmRjYzNiZWEuLi4AAAAAAGNXAAJ5JgQiGEEtUQgwE84MASDbMEGb9mfOQeY/GIRADAEg2zBBm/ZnzkGSXegxStgkCUrKABQoAzpB\u002BCfsjEBXAAERiEoQeNBBm/ZnzkGSXegxStgkCUrKABQoAzpB\u002BCfsjEDo2WhC"; - string manifest = """{"name":"ContractWithVerify","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"_deploy","parameters":[{"name":"data","type":"Any"},{"name":"update","type":"Boolean"}],"returntype":"Void","offset":0,"safe":false},{"name":"verify","parameters":[],"returntype":"Boolean","offset":31,"safe":false},{"name":"verify","parameters":[{"name":"prefix","type":"Integer"}],"returntype":"Boolean","offset":63,"safe":false}],"events":[]},"permissions":[],"trusts":[],"extra":{"nef":{"optimization":"All"}}}"""; - JObject deployResp = (JObject)_rpcServer.InvokeFunction(new JArray([ContractManagement.ContractManagement.Hash.ToString(), - "deploy", - new JArray([ - new JObject() { ["type"] = nameof(ContractParameterType.ByteArray), ["value"] = base64NefFile }, - new JObject() { ["type"] = nameof(ContractParameterType.String), ["value"] = manifest }, - ]), - validatorSigner])); - Assert.AreEqual(deployResp["state"], nameof(VM.VMState.HALT)); - UInt160 deployedScriptHash = new UInt160(Convert.FromBase64String(deployResp["notifications"][0]["state"]["value"][0]["value"].AsString())); - SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); - Transaction? tx = new Transaction - { - Nonce = 233, - ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + _neoSystem.Settings.MaxValidUntilBlockIncrement, - Signers = [new Signer() { Account = ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], - Attributes = Array.Empty(), - Script = Convert.FromBase64String(deployResp["script"].AsString()), - Witnesses = null, - }; - ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: _neoSystem.Settings, gas: 1200_0000_0000); - engine.SnapshotCache.Commit(); - - // invoke verify without signer; should return false - JObject resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString()]); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), false); - // invoke verify with signer; should return true - resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([]), validatorSigner]); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); - // invoke verify with wrong input value; should FAULT - resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "0" }]), validatorSigner]); - Assert.AreEqual(resp["state"], nameof(VM.VMState.FAULT)); - Assert.AreEqual(resp["exception"], "Object reference not set to an instance of an object."); - // invoke verify with 1 param and signer; should return true - resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]); - Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); - Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); - // invoke verify with 2 param (which does not exist); should throw Exception - Assert.ThrowsException(() => _rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }, new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]), - $"Invalid contract verification function - The smart contract {deployedScriptHash} haven't got verify method with 2 input parameters."); - } + string base64NefFile = "TkVGM05lby5Db21waWxlci5DU2hhcnAgMy43LjQrNjAzNGExODIxY2E3MDk0NjBlYzMxMzZjNzBjMmRjYzNiZWEuLi4AAAAAAGNXAAJ5JgQiGEEtUQgwE84MASDbMEGb9mfOQeY/GIRADAEg2zBBm/ZnzkGSXegxStgkCUrKABQoAzpB\u002BCfsjEBXAAERiEoQeNBBm/ZnzkGSXegxStgkCUrKABQoAzpB\u002BCfsjEDo2WhC"; + string manifest = """{"name":"ContractWithVerify","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"_deploy","parameters":[{"name":"data","type":"Any"},{"name":"update","type":"Boolean"}],"returntype":"Void","offset":0,"safe":false},{"name":"verify","parameters":[],"returntype":"Boolean","offset":31,"safe":false},{"name":"verify","parameters":[{"name":"prefix","type":"Integer"}],"returntype":"Boolean","offset":63,"safe":false}],"events":[]},"permissions":[],"trusts":[],"extra":{"nef":{"optimization":"All"}}}"""; + JObject deployResp = (JObject)_rpcServer.InvokeFunction(new JArray([ContractManagement.ContractManagement.Hash.ToString(), + "deploy", + new JArray([ + new JObject() { ["type"] = nameof(ContractParameterType.ByteArray), ["value"] = base64NefFile }, + new JObject() { ["type"] = nameof(ContractParameterType.String), ["value"] = manifest }, + ]), + validatorSigner])); + Assert.AreEqual(deployResp["state"], nameof(VM.VMState.HALT)); + UInt160 deployedScriptHash = new UInt160(Convert.FromBase64String(deployResp["notifications"][0]["state"]["value"][0]["value"].AsString())); + SnapshotCache snapshot = _neoSystem.GetSnapshotCache(); + Transaction? tx = new Transaction + { + Nonce = 233, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + _neoSystem.Settings.MaxValidUntilBlockIncrement, + Signers = [new Signer() { Account = ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], + Attributes = Array.Empty(), + Script = Convert.FromBase64String(deployResp["script"].AsString()), + Witnesses = null, + }; + ApplicationEngine engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: _neoSystem.Settings, gas: 1200_0000_0000); + engine.SnapshotCache.Commit(); + + // invoke verify without signer; should return false + JObject resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString()]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), false); + // invoke verify with signer; should return true + resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([]), validatorSigner]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); + // invoke verify with wrong input value; should FAULT + resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "0" }]), validatorSigner]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.FAULT)); + Assert.AreEqual(resp["exception"], "Object reference not set to an instance of an object."); + // invoke verify with 1 param and signer; should return true + resp = (JObject)_rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]); + Assert.AreEqual(resp["state"], nameof(VM.VMState.HALT)); + Assert.AreEqual(resp["stack"][0]["value"].AsBoolean(), true); + // invoke verify with 2 param (which does not exist); should throw Exception + Assert.ThrowsException(() => _rpcServer.InvokeContractVerify([deployedScriptHash.ToString(), new JArray([new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }, new JObject() { ["type"] = nameof(ContractParameterType.Integer), ["value"] = "32" }]), validatorSigner]), + $"Invalid contract verification function - The smart contract {deployedScriptHash} haven't got verify method with 2 input parameters."); + } - private void TestUtilOpenWallet() - { - try + private void TestUtilOpenWallet() { - const string Path = "wallet.json"; - const string Password = "123456"; - File.WriteAllText(Path, "{\"name\":null,\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[{\"address\":\"NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv\",\"label\":null,\"isDefault\":false,\"lock\":false,\"key\":\"6PYPMrsCJ3D4AXJCFWYT2WMSBGF7dLoaNipW14t4UFAkZw3Z9vQRQV1bEU\",\"contract\":{\"script\":\"DCEDaR\\u002BFVb8lOdiMZ/wCHLiI\\u002Bzuf17YuGFReFyHQhB80yMpBVuezJw==\",\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false},\"extra\":null}],\"extra\":null}"); - var paramsArray = new JArray(Path, Password); - _rpcServer.OpenWallet(paramsArray); + try + { + const string Path = "wallet.json"; + const string Password = "123456"; + File.WriteAllText(Path, "{\"name\":null,\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[{\"address\":\"NVizn8DiExdmnpTQfjiVY3dox8uXg3Vrxv\",\"label\":null,\"isDefault\":false,\"lock\":false,\"key\":\"6PYPMrsCJ3D4AXJCFWYT2WMSBGF7dLoaNipW14t4UFAkZw3Z9vQRQV1bEU\",\"contract\":{\"script\":\"DCEDaR\\u002BFVb8lOdiMZ/wCHLiI\\u002Bzuf17YuGFReFyHQhB80yMpBVuezJw==\",\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false},\"extra\":null}],\"extra\":null}"); + var paramsArray = new JArray(Path, Password); + _rpcServer.OpenWallet(paramsArray); + } + catch (Exception e) + { + Console.WriteLine(e); + } } - catch (Exception e) - { - Console.WriteLine(e); - } - } - private void TestUtilCloseWallet() - { - try + private void TestUtilCloseWallet() { - const string Path = "wallet.json"; - _rpcServer.CloseWallet([]); - File.Delete(Path); + try + { + const string Path = "wallet.json"; + _rpcServer.CloseWallet([]); + File.Delete(Path); + } + catch (Exception e) + { + Console.WriteLine(e); + } } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - private UInt160 TestUtilAddTestContract() - { - var state = TestUtils.GetContract(); - - var storageKey = new StorageKey - { - Id = state.Id, - Key = new byte[] { 0x01 } - }; - var storageItem = new StorageItem + private UInt160 TestUtilAddTestContract() { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } - }; - - var snapshot = _neoSystem.GetSnapshotCache(); - snapshot.AddContract(state.Hash, state); - snapshot.Add(storageKey, storageItem); - snapshot.Commit(); - return state.Hash; + var state = TestUtils.GetContract(); + + var storageKey = new StorageKey + { + Id = state.Id, + Key = new byte[] { 0x01 } + }; + + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } + }; + + var snapshot = _neoSystem.GetSnapshotCache(); + snapshot.AddContract(state.Hash, state); + snapshot.Add(storageKey, storageItem); + snapshot.Commit(); + return state.Hash; + } } } diff --git a/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj b/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj index 3d2031e4d5..1b2d235fd1 100644 --- a/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj +++ b/tests/Neo.Plugins.Storage.Tests/Neo.Plugins.Storage.Tests.csproj @@ -1,14 +1,14 @@ - net8.0 + net9.0 Neo.Plugins.Storage.Tests - - - + + + diff --git a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs index ed44faab53..7717c29538 100644 --- a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs +++ b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Storage.LevelDB; using Neo.Persistence; using System.IO; using System.Linq; @@ -72,6 +73,20 @@ public void TestLevelDb() TestPersistenceRead(levelDbStore.GetStore(path_leveldb), false); } + [TestMethod] + public void TestLevelDbDatabase() + { + using var db = DB.Open(Path.GetRandomFileName(), new() { CreateIfMissing = true }); + + db.Put(WriteOptions.Default, [0x00, 0x00, 0x01], [0x01]); + db.Put(WriteOptions.Default, [0x00, 0x00, 0x02], [0x02]); + db.Put(WriteOptions.Default, [0x00, 0x00, 0x03], [0x03]); + + CollectionAssert.AreEqual(new byte[] { 0x01, }, db.Get(ReadOptions.Default, [0x00, 0x00, 0x01])); + CollectionAssert.AreEqual(new byte[] { 0x02, }, db.Get(ReadOptions.Default, [0x00, 0x00, 0x02])); + CollectionAssert.AreEqual(new byte[] { 0x03, }, db.Get(ReadOptions.Default, [0x00, 0x00, 0x03])); + } + [TestMethod] public void TestLevelDbSnapshot() { diff --git a/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 194d65c35b..00aac10d42 100644 --- a/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -57,13 +57,6 @@ public void TestMurmurReadOnlySpan() input.Murmur128(0).Should().Equal(input2.Murmur128(0)); } - [TestMethod] - public void TestXxHash3() - { - byte[] data = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat("Hello, World!^_^", 16 * 1024))); - data.XxHash3_32().Should().Be(HashCode.Combine(XxHash3.HashToUInt64(data, 40343))); - } - [TestMethod] public void TestSha256() { diff --git a/tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs b/tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs index f8a6b19b34..edcea4dd75 100644 --- a/tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.Extensions; using Neo.IO; using System; using System.Collections; diff --git a/tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs index c205a7e4bd..10c0f64019 100644 --- a/tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/Neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -10,6 +10,7 @@ // modifications are permitted. using FluentAssertions; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index a3f33f26a7..d32e16d0c6 100644 --- a/tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -10,6 +10,7 @@ // modifications are permitted. using FluentAssertions; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs index 224b1cc3f6..3c5e8e6e7d 100644 --- a/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/tests/Neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Persistence; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/IO/UT_IOHelper.cs b/tests/Neo.UnitTests/IO/UT_IOHelper.cs index d9d753e292..514a8daaf5 100644 --- a/tests/Neo.UnitTests/IO/UT_IOHelper.cs +++ b/tests/Neo.UnitTests/IO/UT_IOHelper.cs @@ -31,7 +31,7 @@ public void TestAsSerializableGeneric() 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00 }; - UInt160 result = Neo.IO.Helper.AsSerializable(caseArray); + UInt160 result = caseArray.AsSerializable(); Assert.AreEqual(UInt160.Zero, result); } @@ -44,7 +44,7 @@ public void TestReadFixedBytes() using (BinaryReader reader = new(new MemoryStream(data), Encoding.UTF8, false)) { - byte[] result = Neo.IO.Helper.ReadFixedBytes(reader, 3); + byte[] result = reader.ReadFixedBytes(3); Assert.AreEqual("010203", result.ToHexString()); Assert.AreEqual(3, reader.BaseStream.Position); @@ -54,7 +54,7 @@ public void TestReadFixedBytes() using (BinaryReader reader = new(new MemoryStream(data), Encoding.UTF8, false)) { - byte[] result = Neo.IO.Helper.ReadFixedBytes(reader, 4); + byte[] result = reader.ReadFixedBytes(4); Assert.AreEqual("01020304", result.ToHexString()); Assert.AreEqual(4, reader.BaseStream.Position); @@ -64,7 +64,7 @@ public void TestReadFixedBytes() using (BinaryReader reader = new(new MemoryStream(data), Encoding.UTF8, false)) { - Assert.ThrowsException(() => Neo.IO.Helper.ReadFixedBytes(reader, 5)); + Assert.ThrowsException(() => reader.ReadFixedBytes(5)); Assert.AreEqual(4, reader.BaseStream.Position); } } @@ -87,7 +87,7 @@ public void TestNullableArray() using (MemoryStream stream = new()) using (BinaryWriter writter = new(stream)) { - Neo.IO.Helper.WriteNullableArray(writter, caseArray); + writter.WriteNullableArray(caseArray); data = stream.ToArray(); } @@ -103,14 +103,33 @@ public void TestNullableArray() // Read 100% MemoryReader reader = new(data); - var read = Neo.IO.Helper.ReadNullableArray(ref reader); + var read = reader.ReadNullableArray(); CollectionAssert.AreEqual(caseArray, read); } [TestMethod] public void TestAsSerializable() { - byte[] caseArray = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + byte[] caseArray = [0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00]; ISerializable result = caseArray.AsSerializable(); Assert.AreEqual(UInt160.Zero, result); } @@ -119,8 +138,8 @@ public void TestAsSerializable() public void TestCompression() { var data = new byte[] { 1, 2, 3, 4 }; - var byteArray = Neo.IO.Helper.CompressLz4(data); - var result = Neo.IO.Helper.DecompressLz4(byteArray.Span, byte.MaxValue); + var byteArray = data.CompressLz4(); + var result = byteArray.Span.DecompressLz4(byte.MaxValue); CollectionAssert.AreEqual(result, data); @@ -129,29 +148,29 @@ public void TestCompression() data = new byte[255]; for (int x = 0; x < data.Length; x++) data[x] = 1; - byteArray = Neo.IO.Helper.CompressLz4(data); - result = Neo.IO.Helper.DecompressLz4(byteArray.Span, byte.MaxValue); + byteArray = data.CompressLz4(); + result = byteArray.Span.DecompressLz4(byte.MaxValue); Assert.IsTrue(byteArray.Length < result.Length); CollectionAssert.AreEqual(result, data); // Error max length - Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray.Span, byte.MaxValue - 1)); - Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(byteArray.Span, -1)); + Assert.ThrowsException(() => byteArray.Span.DecompressLz4(byte.MaxValue - 1)); + Assert.ThrowsException(() => byteArray.Span.DecompressLz4(-1)); // Error length byte[] data_wrong = byteArray.ToArray(); data_wrong[0]++; - Assert.ThrowsException(() => Neo.IO.Helper.DecompressLz4(data_wrong, byte.MaxValue)); + Assert.ThrowsException(() => data_wrong.DecompressLz4(byte.MaxValue)); } [TestMethod] public void TestAsSerializableArray() { byte[] byteArray = new UInt160[] { UInt160.Zero }.ToByteArray(); - UInt160[] result = Neo.IO.Helper.AsSerializableArray(byteArray); + UInt160[] result = byteArray.AsSerializableArray(); Assert.AreEqual(1, result.Length); Assert.AreEqual(UInt160.Zero, result[0]); } @@ -161,9 +180,9 @@ public void TestReadSerializable() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.Write(writer, UInt160.Zero); + writer.Write(UInt160.Zero); MemoryReader reader = new(stream.ToArray()); - UInt160 result = Neo.IO.Helper.ReadSerializable(ref reader); + UInt160 result = reader.ReadSerializable(); Assert.AreEqual(UInt160.Zero, result); } @@ -172,9 +191,9 @@ public void TestReadSerializableArray() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.Write(writer, new UInt160[] { UInt160.Zero }); + writer.Write(new UInt160[] { UInt160.Zero }); MemoryReader reader = new(stream.ToArray()); - UInt160[] resultArray = Neo.IO.Helper.ReadSerializableArray(ref reader); + UInt160[] resultArray = reader.ReadSerializableArray(); Assert.AreEqual(1, resultArray.Length); Assert.AreEqual(UInt160.Zero, resultArray[0]); } @@ -184,10 +203,10 @@ public void TestReadVarBytes() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarBytes(writer, new byte[] { 0xAA, 0xAA }); + writer.WriteVarBytes(new byte[] { 0xAA, 0xAA }); stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new(stream); - byte[] byteArray = Neo.IO.Helper.ReadVarBytes(reader, 10); + byte[] byteArray = reader.ReadVarBytes(10); Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xAA, 0xAA }), Encoding.Default.GetString(byteArray)); } @@ -200,30 +219,30 @@ public void TestReadVarInt() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xFFFF); + writer.WriteVarInt(0xFFFF); stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new(stream); - ulong result = Neo.IO.Helper.ReadVarInt(reader, 0xFFFF); + ulong result = reader.ReadVarInt(0xFFFF); Assert.AreEqual((ulong)0xFFFF, result); } else if (i == 1) { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFF); + writer.WriteVarInt(0xFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new(stream); - ulong result = Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); + ulong result = reader.ReadVarInt(0xFFFFFFFF); Assert.AreEqual(0xFFFFFFFF, result); } else { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFFFF); + writer.WriteVarInt(0xFFFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new(stream); - Action action = () => Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); + Action action = () => reader.ReadVarInt(0xFFFFFFFF); action.Should().Throw(); } } @@ -232,7 +251,7 @@ public void TestReadVarInt() [TestMethod] public void TestToArray() { - byte[] byteArray = Neo.IO.Helper.ToArray(UInt160.Zero); + byte[] byteArray = UInt160.Zero.ToArray(); Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00, @@ -254,7 +273,7 @@ public void TestWrite() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.Write(writer, UInt160.Zero); + writer.Write(UInt160.Zero); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -269,7 +288,7 @@ public void TestWriteGeneric() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.Write(writer, new UInt160[] { UInt160.Zero }); + writer.Write(new UInt160[] { UInt160.Zero }); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -288,28 +307,28 @@ public void TestWriteFixedString() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Action action = () => Neo.IO.Helper.WriteFixedString(writer, null, 0); + Action action = () => writer.WriteFixedString(null, 0); action.Should().Throw(); } else if (i == 1) { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Action action = () => Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length - 1); + Action action = () => writer.WriteFixedString("AA", Encoding.UTF8.GetBytes("AA").Length - 1); action.Should().Throw(); } else if (i == 2) { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Action action = () => Neo.IO.Helper.WriteFixedString(writer, "拉拉", Encoding.UTF8.GetBytes("拉拉").Length - 1); + Action action = () => writer.WriteFixedString("拉拉", Encoding.UTF8.GetBytes("拉拉").Length - 1); action.Should().Throw(); } else if (i == 3) { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length + 1); + writer.WriteFixedString("AA", Encoding.UTF8.GetBytes("AA").Length + 1); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -325,7 +344,7 @@ public void TestWriteVarBytes() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarBytes(writer, new byte[] { 0xAA }); + writer.WriteVarBytes(new byte[] { 0xAA }); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -341,14 +360,14 @@ public void TestWriteVarInt() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Action action = () => Neo.IO.Helper.WriteVarInt(writer, -1); + Action action = () => writer.WriteVarInt(-1); action.Should().Throw(); } else if (i == 1) { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xFC); + writer.WriteVarInt(0xFC); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -358,7 +377,7 @@ public void TestWriteVarInt() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xFFFF); + writer.WriteVarInt(0xFFFF); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -369,7 +388,7 @@ public void TestWriteVarInt() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFF); + writer.WriteVarInt(0xFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -380,7 +399,7 @@ public void TestWriteVarInt() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarInt(writer, 0xAEFFFFFFFF); + writer.WriteVarInt(0xAEFFFFFFFF); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); @@ -395,7 +414,7 @@ public void TestWriteVarString() { MemoryStream stream = new(); BinaryWriter writer = new(stream); - Neo.IO.Helper.WriteVarString(writer, "a"); + writer.WriteVarString("a"); stream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); diff --git a/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs b/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs index a3bcefb37c..59242acd1b 100644 --- a/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.Extensions; using Neo.SmartContract; using System; diff --git a/tests/Neo.UnitTests/Neo.UnitTests.csproj b/tests/Neo.UnitTests/Neo.UnitTests.csproj index 54320cd223..1345a0434e 100644 --- a/tests/Neo.UnitTests/Neo.UnitTests.csproj +++ b/tests/Neo.UnitTests/Neo.UnitTests.csproj @@ -1,14 +1,14 @@ - net8.0 + net9.0 true - - - + + + @@ -27,9 +27,9 @@ - - - + + + diff --git a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs index c3c5c2c86f..fbb2ca5106 100644 --- a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs +++ b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_FullNodeCapability.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Capabilities; diff --git a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs index b113d59da7..cb538771dc 100644 --- a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs +++ b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Capabilities; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs index 6e189ad3ec..1308909d73 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_AddrPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs index a0dac53fbe..219c8cc684 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Conflicts.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs index 1a0ad19a9a..28fdcaf068 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs index 76eff8e198..ae5a0e7758 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterAddPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs index f73e9b5595..0661649215 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_FilterLoadPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs index 3aa4049d87..5859d974ce 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlockByIndexPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs index 6752525ac0..ccd4e7aaed 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_GetBlocksPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs index 9ecdd7f5e4..1c11871225 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HeadersPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs index 09454d298f..0bdb4982c8 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs index 4735fddfe1..c799ff8289 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_InvPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs index f491d46749..b1849f1332 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using System; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs index bff21a4e5e..193f14902d 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_NotValidBefore.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs index d2358e947c..5c2f9db90b 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Signers.cs @@ -23,6 +23,85 @@ namespace Neo.UnitTests.Network.P2P.Payloads [TestClass] public class UT_Signers { + [TestMethod] + public void Test_IEquatable() + { + var ecPoint = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1); + var expected = new Signer() + { + Account = UInt160.Zero, + Scopes = WitnessScope.Global, + AllowedContracts = [UInt160.Zero], + AllowedGroups = [ecPoint], + Rules = [ + new WitnessRule + { + Condition = new BooleanCondition + { + Expression = true, + }, + Action = WitnessRuleAction.Allow, + }, + ] + }; + + var actual = new Signer() + { + Account = UInt160.Zero, + Scopes = WitnessScope.Global, + AllowedContracts = [UInt160.Zero], + AllowedGroups = [ecPoint], + Rules = [ + new WitnessRule + { + Condition = new BooleanCondition + { + Expression = true, + }, + Action = WitnessRuleAction.Allow, + }, + ] + }; + + var notEqual = new Signer() + { + Account = UInt160.Zero, + Scopes = WitnessScope.WitnessRules, + AllowedContracts = [], + AllowedGroups = [], + Rules = [] + }; + + var cnull = new Signer + { + Account = null, + Scopes = WitnessScope.Global, + AllowedContracts = null, + AllowedGroups = null, + Rules = null, + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + + //Check null + Assert.AreNotEqual(cnull, notEqual); + Assert.IsFalse(cnull.Equals(notEqual)); + } + + [TestMethod] public void Serialize_Deserialize_Global() { diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index dc910c5142..97bb61a98a 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -825,7 +825,7 @@ public void Transaction_Serialize_Deserialize_Simple() "010000"); // empty witnesses // try to deserialize - var tx2 = sTx.AsSerializable(); + Transaction tx2 = sTx.AsSerializable(); tx2.Version.Should().Be(0x00); tx2.Nonce.Should().Be(0x01020304); @@ -887,7 +887,7 @@ public void Transaction_Serialize_Deserialize_DistinctCosigners() // back to transaction (should fail, due to non-distinct cosigners) Transaction tx2 = null; Assert.ThrowsException(() => - tx2 = Neo.IO.Helper.AsSerializable(sTx) + tx2 = sTx.AsSerializable() ); Assert.IsNull(tx2); } diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs index 113d667296..dd889e95e1 100644 --- a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessContition.cs @@ -13,12 +13,307 @@ using Neo.Cryptography.ECC; using Neo.Json; using Neo.Network.P2P.Payloads.Conditions; +using System.Security.Policy; namespace Neo.UnitTests.Network.P2P.Payloads { [TestClass] public class UT_WitnessCondition { + [TestMethod] + public void Test_IEquatable_ScriptHashCondition() + { + var expected = new ScriptHashCondition + { + Hash = UInt160.Zero, + }; + + var actual = new ScriptHashCondition + { + Hash = UInt160.Zero, + }; + + var notEqual = new ScriptHashCondition + { + Hash = UInt160.Parse("0xfff4f52ca43d6bf4fec8647a60415b183303d961"), + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_GroupCondition() + { + var point = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1); + var expected = new GroupCondition + { + Group = point, + }; + + var actual = new GroupCondition + { + Group = point, + }; + + var notEqual = new GroupCondition + { + Group = ECPoint.Parse("03b209fd4f53a7170ea4444e0ca0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_CalledByGroupCondition() + { + var point = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1); + var expected = new CalledByGroupCondition + { + Group = point, + }; + + var actual = new CalledByGroupCondition + { + Group = point, + }; + + var notEqual = new CalledByGroupCondition + { + Group = ECPoint.Parse("03b209fd4f53a7170ea4444e0ca0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_CalledByEntryCondition() + { + var expected = new CalledByEntryCondition(); + + var actual = new CalledByEntryCondition(); + + var notEqual = new CalledByContractCondition + { + Hash = UInt160.Parse("0xfff4f52ca43d6bf4fec8647a60415b183303d961"), + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_CalledByContractCondition() + { + var expected = new CalledByContractCondition + { + Hash = UInt160.Zero, + }; + + var actual = new CalledByContractCondition + { + Hash = UInt160.Zero, + }; + + var notEqual = new CalledByContractCondition + { + Hash = UInt160.Parse("0xfff4f52ca43d6bf4fec8647a60415b183303d961"), + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_BooleanCondition() + { + var expected = new BooleanCondition + { + Expression = true, + }; + + var actual = new BooleanCondition + { + Expression = true, + }; + + var notEqual = new BooleanCondition + { + Expression = false, + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_AndCondition() + { + var point = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1); + var hash = UInt160.Zero; + var expected = new AndCondition + { + Expressions = new WitnessCondition[] + { + new CalledByContractCondition { Hash = hash }, + new CalledByGroupCondition { Group = point } + } + }; + + var actual = new AndCondition + { + Expressions = new WitnessCondition[] + { + new CalledByContractCondition { Hash = hash }, + new CalledByGroupCondition { Group = point } + } + }; + + var notEqual = new AndCondition + { + Expressions = new WitnessCondition[] + { + new CalledByContractCondition { Hash = hash }, + } + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + + [TestMethod] + public void Test_IEquatable_OrCondition() + { + var point = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1); + var hash = UInt160.Zero; + var expected = new OrCondition + { + Expressions = new WitnessCondition[] + { + new CalledByContractCondition { Hash = hash }, + new CalledByGroupCondition { Group = point } + } + }; + + var actual = new OrCondition + { + Expressions = new WitnessCondition[] + { + new CalledByContractCondition { Hash = hash }, + new CalledByGroupCondition { Group = point } + } + }; + + var notEqual = new OrCondition + { + Expressions = new WitnessCondition[] + { + new CalledByContractCondition { Hash = hash }, + } + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + [TestMethod] public void TestFromJson1() { diff --git a/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessRule.cs b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessRule.cs new file mode 100644 index 0000000000..6d211942c1 --- /dev/null +++ b/tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessRule.cs @@ -0,0 +1,72 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WitnessRule.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 Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads.Conditions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Network.P2P.Payloads +{ + [TestClass] + public class UT_WitnessRule + { + [TestMethod] + public void Test_IEquatable() + { + var expected = new WitnessRule + { + Action = WitnessRuleAction.Allow, + Condition = new BooleanCondition + { + Expression = true, + } + }; + + var actual = new WitnessRule + { + Action = WitnessRuleAction.Allow, + Condition = new BooleanCondition + { + Expression = true, + } + }; + + var notEqual = new WitnessRule + { + Action = WitnessRuleAction.Deny, + Condition = new BooleanCondition + { + Expression = false, + } + }; + + Assert.IsTrue(expected.Equals(expected)); + + Assert.AreEqual(expected, actual); + Assert.IsTrue(expected == actual); + Assert.IsTrue(expected.Equals(actual)); + + Assert.AreNotEqual(expected, notEqual); + Assert.IsTrue(expected != notEqual); + Assert.IsFalse(expected.Equals(notEqual)); + + Assert.IsFalse(expected == null); + Assert.IsFalse(null == expected); + Assert.AreNotEqual(expected, null); + Assert.IsFalse(expected.Equals(null)); + } + } +} diff --git a/tests/Neo.UnitTests/Network/P2P/UT_Message.cs b/tests/Neo.UnitTests/Network/P2P/UT_Message.cs index 35f99fc44f..7a43cf89a4 100644 --- a/tests/Neo.UnitTests/Network/P2P/UT_Message.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_Message.cs @@ -12,6 +12,7 @@ using Akka.IO; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 9f114e2708..cfa94a3113 100644 --- a/tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/Neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -13,6 +13,7 @@ using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Capabilities; diff --git a/tests/Neo.UnitTests/Persistence/TestMemoryStoreProvider.cs b/tests/Neo.UnitTests/Persistence/TestMemoryStoreProvider.cs index 59b8ac885d..d51398cc0b 100644 --- a/tests/Neo.UnitTests/Persistence/TestMemoryStoreProvider.cs +++ b/tests/Neo.UnitTests/Persistence/TestMemoryStoreProvider.cs @@ -11,11 +11,12 @@ using Neo.Persistence; -namespace Neo.UnitTests.Persistence; - -public class TestMemoryStoreProvider(MemoryStore memoryStore) : IStoreProvider +namespace Neo.UnitTests.Persistence { - public MemoryStore MemoryStore { get; set; } = memoryStore; - public string Name => nameof(MemoryStore); - public IStore GetStore(string path) => MemoryStore; + public class TestMemoryStoreProvider(MemoryStore memoryStore) : IStoreProvider + { + public MemoryStore MemoryStore { get; set; } = memoryStore; + public string Name => nameof(MemoryStore); + public IStore GetStore(string path) => MemoryStore; + } } diff --git a/tests/Neo.UnitTests/Persistence/UT_MemoryClonedCache.cs b/tests/Neo.UnitTests/Persistence/UT_MemoryClonedCache.cs index 56afdc31f1..263aa41401 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemoryClonedCache.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemoryClonedCache.cs @@ -13,115 +13,116 @@ using Neo.Persistence; using Neo.SmartContract; -namespace Neo.UnitTests.Persistence; - -/// -/// When adding data to `datacache` , -/// it gets passed to `snapshotcache` during commit. -/// If `snapshotcache` commits, the data is then passed -/// to the underlying store . -/// However, because snapshots are immutable, the new data -/// cannot be retrieved from the snapshot . -/// -/// When deleting data from `datacache` , -/// it won't exist in `datacache` upon commit, and therefore will be removed from `snapshotcache` . -/// Upon `snapshotcache` commit, the data is deleted from the store . -/// However, since the snapshot remains unchanged, the data still exists in the snapshot. -/// If you attempt to read this data from `datacache` or `snapshotcache` , -/// which do not have the data, they will retrieve it from the snapshot instead of the store. -/// Thus, they can still access data that has been deleted. -/// -[TestClass] -public class UT_MemoryClonedCache +namespace Neo.UnitTests.Persistence { - private MemoryStore _memoryStore; - private MemorySnapshot _snapshot; - private SnapshotCache _snapshotCache; - private DataCache _dataCache; - - [TestInitialize] - public void Setup() - { - _memoryStore = new MemoryStore(); - _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; - _snapshotCache = new SnapshotCache(_snapshot); - _dataCache = _snapshotCache.CreateSnapshot(); - } - - [TestCleanup] - public void CleanUp() - { - _dataCache.Commit(); - _snapshotCache.Commit(); - _memoryStore.Reset(); - } - - [TestMethod] - public void SingleSnapshotCacheTest() + /// + /// When adding data to `datacache` , + /// it gets passed to `snapshotcache` during commit. + /// If `snapshotcache` commits, the data is then passed + /// to the underlying store . + /// However, because snapshots are immutable, the new data + /// cannot be retrieved from the snapshot . + /// + /// When deleting data from `datacache` , + /// it won't exist in `datacache` upon commit, and therefore will be removed from `snapshotcache` . + /// Upon `snapshotcache` commit, the data is deleted from the store . + /// However, since the snapshot remains unchanged, the data still exists in the snapshot. + /// If you attempt to read this data from `datacache` or `snapshotcache` , + /// which do not have the data, they will retrieve it from the snapshot instead of the store. + /// Thus, they can still access data that has been deleted. + /// + [TestClass] + public class UT_MemoryClonedCache { - var key1 = new KeyBuilder(0, 1); - var value1 = new StorageItem([0x03, 0x04]); - - Assert.IsFalse(_dataCache.Contains(key1)); - _dataCache.Add(key1, value1); - - Assert.IsTrue(_dataCache.Contains(key1)); - Assert.IsFalse(_snapshotCache.Contains(key1)); - Assert.IsFalse(_snapshot.Contains(key1.ToArray())); - Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); - - // After the data cache is committed, it should be dropped - // so its value after the commit is meaningless and should not be used. - _dataCache.Commit(); - - Assert.IsTrue(_dataCache.Contains(key1)); - Assert.IsTrue(_snapshotCache.Contains(key1)); - Assert.IsFalse(_snapshot.Contains(key1.ToArray())); - Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); - - // After the snapshot is committed, it should be dropped - // so its value after the commit is meaningless and should not be used. - _snapshotCache.Commit(); - - Assert.IsTrue(_dataCache.Contains(key1)); - Assert.IsTrue(_snapshotCache.Contains(key1)); - Assert.IsFalse(_snapshot.Contains(key1.ToArray())); - Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); - - // Test delete - - // Reset the snapshot to make it accessible to the new value. - _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; - _snapshotCache = new SnapshotCache(_snapshot); - _dataCache = _snapshotCache.CreateSnapshot(); - - Assert.IsTrue(_dataCache.Contains(key1)); - _dataCache.Delete(key1); - - Assert.IsFalse(_dataCache.Contains(key1)); - Assert.IsTrue(_snapshotCache.Contains(key1)); - Assert.IsTrue(_snapshot.Contains(key1.ToArray())); - Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); - - // After the data cache is committed, it should be dropped - // so its value after the commit is meaningless and should not be used. - _dataCache.Commit(); - - Assert.IsFalse(_dataCache.Contains(key1)); - Assert.IsFalse(_snapshotCache.Contains(key1)); - Assert.IsTrue(_snapshot.Contains(key1.ToArray())); - Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); - - - // After the snapshot cache is committed, it should be dropped - // so its value after the commit is meaningless and should not be used. - _snapshotCache.Commit(); - - // The reason that datacache, snapshotcache still contains key1 is because - // they can not find the value from its cache, so they fetch it from the snapshot of the store. - Assert.IsTrue(_dataCache.Contains(key1)); - Assert.IsTrue(_snapshotCache.Contains(key1)); - Assert.IsTrue(_snapshot.Contains(key1.ToArray())); - Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); + private MemoryStore _memoryStore; + private MemorySnapshot _snapshot; + private SnapshotCache _snapshotCache; + private DataCache _dataCache; + + [TestInitialize] + public void Setup() + { + _memoryStore = new MemoryStore(); + _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; + _snapshotCache = new SnapshotCache(_snapshot); + _dataCache = _snapshotCache.CreateSnapshot(); + } + + [TestCleanup] + public void CleanUp() + { + _dataCache.Commit(); + _snapshotCache.Commit(); + _memoryStore.Reset(); + } + + [TestMethod] + public void SingleSnapshotCacheTest() + { + var key1 = new KeyBuilder(0, 1); + var value1 = new StorageItem([0x03, 0x04]); + + Assert.IsFalse(_dataCache.Contains(key1)); + _dataCache.Add(key1, value1); + + Assert.IsTrue(_dataCache.Contains(key1)); + Assert.IsFalse(_snapshotCache.Contains(key1)); + Assert.IsFalse(_snapshot.Contains(key1.ToArray())); + Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); + + // After the data cache is committed, it should be dropped + // so its value after the commit is meaningless and should not be used. + _dataCache.Commit(); + + Assert.IsTrue(_dataCache.Contains(key1)); + Assert.IsTrue(_snapshotCache.Contains(key1)); + Assert.IsFalse(_snapshot.Contains(key1.ToArray())); + Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); + + // After the snapshot is committed, it should be dropped + // so its value after the commit is meaningless and should not be used. + _snapshotCache.Commit(); + + Assert.IsTrue(_dataCache.Contains(key1)); + Assert.IsTrue(_snapshotCache.Contains(key1)); + Assert.IsFalse(_snapshot.Contains(key1.ToArray())); + Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); + + // Test delete + + // Reset the snapshot to make it accessible to the new value. + _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; + _snapshotCache = new SnapshotCache(_snapshot); + _dataCache = _snapshotCache.CreateSnapshot(); + + Assert.IsTrue(_dataCache.Contains(key1)); + _dataCache.Delete(key1); + + Assert.IsFalse(_dataCache.Contains(key1)); + Assert.IsTrue(_snapshotCache.Contains(key1)); + Assert.IsTrue(_snapshot.Contains(key1.ToArray())); + Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); + + // After the data cache is committed, it should be dropped + // so its value after the commit is meaningless and should not be used. + _dataCache.Commit(); + + Assert.IsFalse(_dataCache.Contains(key1)); + Assert.IsFalse(_snapshotCache.Contains(key1)); + Assert.IsTrue(_snapshot.Contains(key1.ToArray())); + Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); + + + // After the snapshot cache is committed, it should be dropped + // so its value after the commit is meaningless and should not be used. + _snapshotCache.Commit(); + + // The reason that datacache, snapshotcache still contains key1 is because + // they can not find the value from its cache, so they fetch it from the snapshot of the store. + Assert.IsTrue(_dataCache.Contains(key1)); + Assert.IsTrue(_snapshotCache.Contains(key1)); + Assert.IsTrue(_snapshot.Contains(key1.ToArray())); + Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); + } } } diff --git a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs index 8eea33c9a7..f2a089d9fa 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs @@ -13,117 +13,118 @@ using Neo.Persistence; using System.Linq; -namespace Neo.UnitTests.Persistence; - -[TestClass] -public class UT_MemorySnapshot +namespace Neo.UnitTests.Persistence { - private MemoryStore _memoryStore; - private MemorySnapshot _snapshot; - - [TestInitialize] - public void Setup() - { - _memoryStore = new MemoryStore(); - _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; - } - - [TestCleanup] - public void CleanUp() - { - _memoryStore.Reset(); - } - - [TestMethod] - public void SingleSnapshotTest() - { - var key1 = new byte[] { 0x01, 0x02 }; - var value1 = new byte[] { 0x03, 0x04 }; - - _snapshot.Delete(key1); - Assert.IsNull(_snapshot.TryGet(key1)); - - // Both Store and Snapshot can not get the value that are cached in the snapshot - _snapshot.Put(key1, value1); - Assert.IsNull(_snapshot.TryGet(key1)); - Assert.IsNull(_memoryStore.TryGet(key1)); - - _snapshot.Commit(); - - // After commit the snapshot, the value can be get from the store but still can not get from the snapshot - CollectionAssert.AreEqual(value1, _memoryStore.TryGet(key1)); - Assert.IsNull(_snapshot.TryGet(key1)); - - _snapshot.Delete(key1); - - // Deleted value can not be found from the snapshot but can still get from the store - // This is because snapshot has no key1 at all. - Assert.IsFalse(_snapshot.Contains(key1)); - Assert.IsTrue(_memoryStore.Contains(key1)); - - _snapshot.Commit(); - - // After commit the snapshot, the value can not be found from the store - Assert.IsFalse(_memoryStore.Contains(key1)); - - // Test seek in order - _snapshot.Put([0x00, 0x00, 0x04], [0x04]); - _snapshot.Put([0x00, 0x00, 0x00], [0x00]); - _snapshot.Put([0x00, 0x00, 0x01], [0x01]); - _snapshot.Put([0x00, 0x00, 0x02], [0x02]); - _snapshot.Put([0x00, 0x00, 0x03], [0x03]); - - // Can not get anything from the snapshot - var entries = _snapshot.Seek([0x00, 0x00, 0x02]).ToArray(); - Assert.AreEqual(0, entries.Length); - } - - [TestMethod] - public void MultiSnapshotTest() + [TestClass] + public class UT_MemorySnapshot { - var key1 = new byte[] { 0x01, 0x02 }; - var value1 = new byte[] { 0x03, 0x04 }; - - _snapshot.Delete(key1); - Assert.IsNull(_snapshot.TryGet(key1)); - - // Both Store and Snapshot can not get the value that are cached in the snapshot - _snapshot.Put(key1, value1); - // After commit the snapshot, the value can be get from the store but still can not get from the snapshot - // But can get the value from a new snapshot - _snapshot.Commit(); - var snapshot2 = _memoryStore.GetSnapshot(); - CollectionAssert.AreEqual(value1, _memoryStore.TryGet(key1)); - Assert.IsNull(_snapshot.TryGet(key1)); - CollectionAssert.AreEqual(value1, snapshot2.TryGet(key1)); - - Assert.IsFalse(_snapshot.TryGet(key1, out var value2)); - - Assert.IsTrue(snapshot2.TryGet(key1, out value2)); - CollectionAssert.AreEqual(value1, value2); - - Assert.IsTrue(_memoryStore.TryGet(key1, out value2)); - CollectionAssert.AreEqual(value1, value2); - - _snapshot.Delete(key1); - - // Deleted value can not being found from the snapshot but can still get from the store and snapshot2 - Assert.IsFalse(_snapshot.Contains(key1)); - Assert.IsTrue(_memoryStore.Contains(key1)); - Assert.IsTrue(snapshot2.Contains(key1)); - - _snapshot.Commit(); - - // After commit the snapshot, the value can not be found from the store, but can be found in snapshots - // Cause snapshot1 or store can not change the status of snapshot2. - Assert.IsFalse(_memoryStore.Contains(key1)); - Assert.IsTrue(snapshot2.Contains(key1)); - Assert.IsFalse(_snapshot.Contains(key1)); - - // Add value via snapshot2 will not affect snapshot1 at all - snapshot2.Put(key1, value1); - snapshot2.Commit(); - Assert.IsNull(_snapshot.TryGet(key1)); - CollectionAssert.AreEqual(value1, snapshot2.TryGet(key1)); + private MemoryStore _memoryStore; + private MemorySnapshot _snapshot; + + [TestInitialize] + public void Setup() + { + _memoryStore = new MemoryStore(); + _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; + } + + [TestCleanup] + public void CleanUp() + { + _memoryStore.Reset(); + } + + [TestMethod] + public void SingleSnapshotTest() + { + var key1 = new byte[] { 0x01, 0x02 }; + var value1 = new byte[] { 0x03, 0x04 }; + + _snapshot.Delete(key1); + Assert.IsNull(_snapshot.TryGet(key1)); + + // Both Store and Snapshot can not get the value that are cached in the snapshot + _snapshot.Put(key1, value1); + Assert.IsNull(_snapshot.TryGet(key1)); + Assert.IsNull(_memoryStore.TryGet(key1)); + + _snapshot.Commit(); + + // After commit the snapshot, the value can be get from the store but still can not get from the snapshot + CollectionAssert.AreEqual(value1, _memoryStore.TryGet(key1)); + Assert.IsNull(_snapshot.TryGet(key1)); + + _snapshot.Delete(key1); + + // Deleted value can not be found from the snapshot but can still get from the store + // This is because snapshot has no key1 at all. + Assert.IsFalse(_snapshot.Contains(key1)); + Assert.IsTrue(_memoryStore.Contains(key1)); + + _snapshot.Commit(); + + // After commit the snapshot, the value can not be found from the store + Assert.IsFalse(_memoryStore.Contains(key1)); + + // Test seek in order + _snapshot.Put([0x00, 0x00, 0x04], [0x04]); + _snapshot.Put([0x00, 0x00, 0x00], [0x00]); + _snapshot.Put([0x00, 0x00, 0x01], [0x01]); + _snapshot.Put([0x00, 0x00, 0x02], [0x02]); + _snapshot.Put([0x00, 0x00, 0x03], [0x03]); + + // Can not get anything from the snapshot + var entries = _snapshot.Seek([0x00, 0x00, 0x02]).ToArray(); + Assert.AreEqual(0, entries.Length); + } + + [TestMethod] + public void MultiSnapshotTest() + { + var key1 = new byte[] { 0x01, 0x02 }; + var value1 = new byte[] { 0x03, 0x04 }; + + _snapshot.Delete(key1); + Assert.IsNull(_snapshot.TryGet(key1)); + + // Both Store and Snapshot can not get the value that are cached in the snapshot + _snapshot.Put(key1, value1); + // After commit the snapshot, the value can be get from the store but still can not get from the snapshot + // But can get the value from a new snapshot + _snapshot.Commit(); + var snapshot2 = _memoryStore.GetSnapshot(); + CollectionAssert.AreEqual(value1, _memoryStore.TryGet(key1)); + Assert.IsNull(_snapshot.TryGet(key1)); + CollectionAssert.AreEqual(value1, snapshot2.TryGet(key1)); + + Assert.IsFalse(_snapshot.TryGet(key1, out var value2)); + + Assert.IsTrue(snapshot2.TryGet(key1, out value2)); + CollectionAssert.AreEqual(value1, value2); + + Assert.IsTrue(_memoryStore.TryGet(key1, out value2)); + CollectionAssert.AreEqual(value1, value2); + + _snapshot.Delete(key1); + + // Deleted value can not being found from the snapshot but can still get from the store and snapshot2 + Assert.IsFalse(_snapshot.Contains(key1)); + Assert.IsTrue(_memoryStore.Contains(key1)); + Assert.IsTrue(snapshot2.Contains(key1)); + + _snapshot.Commit(); + + // After commit the snapshot, the value can not be found from the store, but can be found in snapshots + // Cause snapshot1 or store can not change the status of snapshot2. + Assert.IsFalse(_memoryStore.Contains(key1)); + Assert.IsTrue(snapshot2.Contains(key1)); + Assert.IsFalse(_snapshot.Contains(key1)); + + // Add value via snapshot2 will not affect snapshot1 at all + snapshot2.Put(key1, value1); + snapshot2.Commit(); + Assert.IsNull(_snapshot.TryGet(key1)); + CollectionAssert.AreEqual(value1, snapshot2.TryGet(key1)); + } } } diff --git a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshotCache.cs b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshotCache.cs index 8c4843c3f3..10eb69e562 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshotCache.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshotCache.cs @@ -14,121 +14,122 @@ using Neo.SmartContract; using System.Linq; -namespace Neo.UnitTests.Persistence; - -[TestClass] -public class UT_MemorySnapshotCache +namespace Neo.UnitTests.Persistence { - private MemoryStore _memoryStore; - private MemorySnapshot _snapshot; - private SnapshotCache _snapshotCache; - - [TestInitialize] - public void Setup() - { - _memoryStore = new MemoryStore(); - _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; - _snapshotCache = new SnapshotCache(_snapshot); - } - - [TestCleanup] - public void CleanUp() - { - _snapshotCache.Commit(); - _memoryStore.Reset(); - } - - [TestMethod] - public void SingleSnapshotCacheTest() + [TestClass] + public class UT_MemorySnapshotCache { - var key1 = new KeyBuilder(0, 1); - var value1 = new StorageItem([0x03, 0x04]); - - _snapshotCache.Delete(key1); - Assert.IsNull(_snapshotCache.TryGet(key1)); - - // Adding value to the snapshot cache will not affect the snapshot or the store - // But the snapshot cache itself can see the added item right after it is added. - _snapshotCache.Add(key1, value1); - - Assert.AreEqual(value1.Value, _snapshotCache.TryGet(key1).Value); - Assert.IsNull(_snapshot.TryGet(key1.ToArray())); - Assert.IsNull(_memoryStore.TryGet(key1.ToArray())); - - // After commit the snapshot cache, it works the same as commit the snapshot. - // the value can be get from the snapshot cache and store but still can not get from the snapshot - _snapshotCache.Commit(); - - Assert.AreEqual(value1.Value, _snapshotCache.TryGet(key1).Value); - Assert.IsFalse(_snapshot.Contains(key1.ToArray())); - Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); - - // Test delete - - // Reset the snapshot to make it accessible to the new value. - _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; - _snapshotCache = new SnapshotCache(_snapshot); - - // Delete value to the snapshot cache will not affect the snapshot or the store - // But the snapshot cache itself can not see the added item. - _snapshotCache.Delete(key1); - - // Value is removed from the snapshot cache immediately - Assert.IsNull(_snapshotCache.TryGet(key1)); - // But the underline snapshot will not be changed. - Assert.IsTrue(_snapshot.Contains(key1.ToArray())); - // And the store is also not affected. - Assert.IsNotNull(_memoryStore.TryGet(key1.ToArray())); - - // commit the snapshot cache - _snapshotCache.Commit(); - - // Value is removed from both the store, but the snapshot and snapshot cache remains the same. - Assert.IsTrue(_snapshotCache.Contains(key1)); - Assert.IsTrue(_snapshot.Contains(key1.ToArray())); - Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); - } - - [TestMethod] - public void MultiSnapshotCacheTest() - { - var key1 = new KeyBuilder(0, 1); - var value1 = new StorageItem([0x03, 0x04]); - - _snapshotCache.Delete(key1); - Assert.IsNull(_snapshotCache.TryGet(key1)); - - // Adding value to the snapshot cache will not affect the snapshot or the store - // But the snapshot cache itself can see the added item. - _snapshotCache.Add(key1, value1); - - // After commit the snapshot cache, it works the same as commit the snapshot. - // the value can be get from the snapshot cache but still can not get from the snapshot - _snapshotCache.Commit(); - - // Get a new snapshot cache to test if the value can be seen from the new snapshot cache - var snapshotCache2 = new SnapshotCache(_snapshot); - Assert.IsNull(snapshotCache2.TryGet(key1)); - Assert.IsFalse(_snapshot.Contains(key1.ToArray())); - - // Test delete - - // Reset the snapshot to make it accessible to the new value. - _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; - _snapshotCache = new SnapshotCache(_snapshot); - - // Delete value to the snapshot cache will affect the snapshot - // But the snapshot and store itself can still see the item. - _snapshotCache.Delete(key1); - - // Commiting the snapshot cache will change the store, but the existing snapshot remains same. - _snapshotCache.Commit(); - - // reset the snapshotcache2 to snapshot - snapshotCache2 = new SnapshotCache(_snapshot); - // Value is removed from the store, but the snapshot remains the same. - // thus the snapshot cache from the snapshot will remain the same. - Assert.IsNotNull(snapshotCache2.TryGet(key1)); - Assert.IsNull(_memoryStore.TryGet(key1.ToArray())); + private MemoryStore _memoryStore; + private MemorySnapshot _snapshot; + private SnapshotCache _snapshotCache; + + [TestInitialize] + public void Setup() + { + _memoryStore = new MemoryStore(); + _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; + _snapshotCache = new SnapshotCache(_snapshot); + } + + [TestCleanup] + public void CleanUp() + { + _snapshotCache.Commit(); + _memoryStore.Reset(); + } + + [TestMethod] + public void SingleSnapshotCacheTest() + { + var key1 = new KeyBuilder(0, 1); + var value1 = new StorageItem([0x03, 0x04]); + + _snapshotCache.Delete(key1); + Assert.IsNull(_snapshotCache.TryGet(key1)); + + // Adding value to the snapshot cache will not affect the snapshot or the store + // But the snapshot cache itself can see the added item right after it is added. + _snapshotCache.Add(key1, value1); + + Assert.AreEqual(value1.Value, _snapshotCache.TryGet(key1).Value); + Assert.IsNull(_snapshot.TryGet(key1.ToArray())); + Assert.IsNull(_memoryStore.TryGet(key1.ToArray())); + + // After commit the snapshot cache, it works the same as commit the snapshot. + // the value can be get from the snapshot cache and store but still can not get from the snapshot + _snapshotCache.Commit(); + + Assert.AreEqual(value1.Value, _snapshotCache.TryGet(key1).Value); + Assert.IsFalse(_snapshot.Contains(key1.ToArray())); + Assert.IsTrue(_memoryStore.Contains(key1.ToArray())); + + // Test delete + + // Reset the snapshot to make it accessible to the new value. + _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; + _snapshotCache = new SnapshotCache(_snapshot); + + // Delete value to the snapshot cache will not affect the snapshot or the store + // But the snapshot cache itself can not see the added item. + _snapshotCache.Delete(key1); + + // Value is removed from the snapshot cache immediately + Assert.IsNull(_snapshotCache.TryGet(key1)); + // But the underline snapshot will not be changed. + Assert.IsTrue(_snapshot.Contains(key1.ToArray())); + // And the store is also not affected. + Assert.IsNotNull(_memoryStore.TryGet(key1.ToArray())); + + // commit the snapshot cache + _snapshotCache.Commit(); + + // Value is removed from both the store, but the snapshot and snapshot cache remains the same. + Assert.IsTrue(_snapshotCache.Contains(key1)); + Assert.IsTrue(_snapshot.Contains(key1.ToArray())); + Assert.IsFalse(_memoryStore.Contains(key1.ToArray())); + } + + [TestMethod] + public void MultiSnapshotCacheTest() + { + var key1 = new KeyBuilder(0, 1); + var value1 = new StorageItem([0x03, 0x04]); + + _snapshotCache.Delete(key1); + Assert.IsNull(_snapshotCache.TryGet(key1)); + + // Adding value to the snapshot cache will not affect the snapshot or the store + // But the snapshot cache itself can see the added item. + _snapshotCache.Add(key1, value1); + + // After commit the snapshot cache, it works the same as commit the snapshot. + // the value can be get from the snapshot cache but still can not get from the snapshot + _snapshotCache.Commit(); + + // Get a new snapshot cache to test if the value can be seen from the new snapshot cache + var snapshotCache2 = new SnapshotCache(_snapshot); + Assert.IsNull(snapshotCache2.TryGet(key1)); + Assert.IsFalse(_snapshot.Contains(key1.ToArray())); + + // Test delete + + // Reset the snapshot to make it accessible to the new value. + _snapshot = _memoryStore.GetSnapshot() as MemorySnapshot; + _snapshotCache = new SnapshotCache(_snapshot); + + // Delete value to the snapshot cache will affect the snapshot + // But the snapshot and store itself can still see the item. + _snapshotCache.Delete(key1); + + // Commiting the snapshot cache will change the store, but the existing snapshot remains same. + _snapshotCache.Commit(); + + // reset the snapshotcache2 to snapshot + snapshotCache2 = new SnapshotCache(_snapshot); + // Value is removed from the store, but the snapshot remains the same. + // thus the snapshot cache from the snapshot will remain the same. + Assert.IsNotNull(snapshotCache2.TryGet(key1)); + Assert.IsNull(_memoryStore.TryGet(key1.ToArray())); + } } } diff --git a/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs index 4473ae63a8..07f8740776 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Persistence; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index fdbcf671b2..7a44517b87 100644 --- a/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/Neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -18,6 +18,7 @@ using Neo.SmartContract.Manifest; using Neo.VM; using System; +using System.Linq; namespace Neo.UnitTests.SmartContract.Manifest { @@ -74,6 +75,22 @@ public void ParseFromJson_Permissions() Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); } + [TestMethod] + public void EqualTests() + { + var json = @"{""name"":""testManifest"",""groups"":[],""features"":{},""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""extra"":null}"; + var manifestA = ContractManifest.Parse(json); + var manifestB = ContractManifest.Parse(json); + + Assert.IsTrue(manifestA.Abi.Methods.SequenceEqual(manifestB.Abi.Methods)); + + for (int x = 0; x < manifestA.Abi.Methods.Length; x++) + { + Assert.IsTrue(manifestA.Abi.Methods[x] == manifestB.Abi.Methods[x]); + Assert.IsFalse(manifestA.Abi.Methods[x] != manifestB.Abi.Methods[x]); + } + } + [TestMethod] public void ParseFromJson_SafeMethods() { diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs index a7d087fa86..b6e41c559f 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index f9a3089f9d..64d6338f0c 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 745670977e..d5e6f22800 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs index f9dc5e7b46..2d71b97e25 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.SmartContract; using Neo.VM; using System.Linq; diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index def978596e..26df1a1639 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.UnitTests.Extensions; diff --git a/tests/Neo.UnitTests/SmartContract/UT_Contract.cs b/tests/Neo.UnitTests/SmartContract/UT_Contract.cs index 07ae320a0f..ecd8532faa 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Contract.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; diff --git a/tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs b/tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs index a3514c7f5a..029514c103 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_KeyBuilder.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Extensions; +using Neo.IO; using Neo.SmartContract; namespace Neo.UnitTests.SmartContract @@ -42,5 +43,71 @@ public void Test() key = key.AddBigEndian(1); Assert.AreEqual("010000000000000001", key.ToArray().ToHexString()); } + + [TestMethod] + public void TestAddInt() + { + var key = new KeyBuilder(1, 2); + Assert.AreEqual("0100000002", key.ToArray().ToHexString()); + + // add int + key = new KeyBuilder(1, 2); + key = key.AddBigEndian(-1); + key = key.AddBigEndian(2); + key = key.AddBigEndian(3); + Assert.AreEqual("0100000002ffffffff0000000200000003", key.ToArray().ToHexString()); + + // add ulong + key = new KeyBuilder(1, 2); + key = key.AddBigEndian(1ul); + key = key.AddBigEndian(2ul); + key = key.AddBigEndian(ulong.MaxValue); + Assert.AreEqual("010000000200000000000000010000000000000002ffffffffffffffff", key.ToArray().ToHexString()); + + // add uint + key = new KeyBuilder(1, 2); + key = key.AddBigEndian(1u); + key = key.AddBigEndian(2u); + key = key.AddBigEndian(uint.MaxValue); + Assert.AreEqual("01000000020000000100000002ffffffff", key.ToArray().ToHexString()); + + // add byte + key = new KeyBuilder(1, 2); + key = key.Add((byte)1); + key = key.Add((byte)2); + key = key.Add((byte)3); + Assert.AreEqual("0100000002010203", key.ToArray().ToHexString()); + } + + [TestMethod] + public void TestAddUInt() + { + var key = new KeyBuilder(1, 2); + var value = new byte[UInt160.Length]; + for (int i = 0; i < value.Length; i++) + value[i] = (byte)i; + + key = key.Add(new UInt160(value)); + Assert.AreEqual("0100000002000102030405060708090a0b0c0d0e0f10111213", key.ToArray().ToHexString()); + + var key2 = new KeyBuilder(1, 2); + key2 = key2.Add((ISerializable)(new UInt160(value))); + + // It must be same before and after optimization. + Assert.AreEqual(key.ToArray().ToHexString(), key2.ToArray().ToHexString()); + + key = new KeyBuilder(1, 2); + value = new byte[UInt256.Length]; + for (int i = 0; i < value.Length; i++) + value[i] = (byte)i; + key = key.Add(new UInt256(value)); + Assert.AreEqual("0100000002000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", key.ToArray().ToHexString()); + + key2 = new KeyBuilder(1, 2); + key2 = key2.Add((ISerializable)(new UInt256(value))); + + // It must be same before and after optimization. + Assert.AreEqual(key.ToArray().ToHexString(), key2.ToArray().ToHexString()); + } } } diff --git a/tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs b/tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs index 9d687e1053..b6be60cdab 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_MethodToken.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.SmartContract; using System; diff --git a/tests/Neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/Neo.UnitTests/SmartContract/UT_NefFile.cs index 40142e93be..b5fb90fe22 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_NefFile.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_NefFile.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using Neo.SmartContract; using System; diff --git a/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs index a864c5d427..e2f89751c1 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -12,6 +12,7 @@ using Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/Neo.UnitTests/TestUtils.Block.cs b/tests/Neo.UnitTests/TestUtils.Block.cs index dd1ad77625..e1dcf42af9 100644 --- a/tests/Neo.UnitTests/TestUtils.Block.cs +++ b/tests/Neo.UnitTests/TestUtils.Block.cs @@ -24,176 +24,177 @@ using System.Collections.Generic; using System.Linq; -namespace Neo.UnitTests; - -public partial class TestUtils +namespace Neo.UnitTests { - const byte Prefix_Block = 5; - const byte Prefix_BlockHash = 9; - const byte Prefix_Transaction = 11; - const byte Prefix_CurrentBlock = 12; - - /// - /// Test Util function SetupHeaderWithValues - /// - /// The snapshot of the current storage provider. Can be null. - /// The header to be assigned - /// PrevHash - /// MerkleRoot - /// NextConsensus - /// Timestamp - /// Index - /// Nonce - /// Witness - public static void SetupHeaderWithValues(DataCache snapshot, Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out ulong nonceVal, out uint indexVal, out Witness scriptVal) + public partial class TestUtils { - header.PrevHash = val256; - header.MerkleRoot = merkRootVal = UInt256.Parse("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); - header.Timestamp = timestampVal = new DateTime(2024, 06, 05, 0, 33, 1, 001, DateTimeKind.Utc).ToTimestampMS(); - if (snapshot != null) - header.Index = indexVal = NativeContract.Ledger.CurrentIndex(snapshot) + 1; - else - header.Index = indexVal = 0; - header.Nonce = nonceVal = 0; - header.NextConsensus = val160 = UInt160.Zero; - header.Witness = scriptVal = new Witness + const byte Prefix_Block = 5; + const byte Prefix_BlockHash = 9; + const byte Prefix_Transaction = 11; + const byte Prefix_CurrentBlock = 12; + + /// + /// Test Util function SetupHeaderWithValues + /// + /// The snapshot of the current storage provider. Can be null. + /// The header to be assigned + /// PrevHash + /// MerkleRoot + /// NextConsensus + /// Timestamp + /// Index + /// Nonce + /// Witness + public static void SetupHeaderWithValues(DataCache snapshot, Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out ulong nonceVal, out uint indexVal, out Witness scriptVal) { - InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSH1 } - }; - } - - public static void SetupBlockWithValues(DataCache snapshot, Block block, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out ulong nonceVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, int numberOfTransactions) - { - Header header = new Header(); - SetupHeaderWithValues(snapshot, header, val256, out merkRootVal, out val160, out timestampVal, out nonceVal, out indexVal, out scriptVal); + header.PrevHash = val256; + header.MerkleRoot = merkRootVal = UInt256.Parse("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); + header.Timestamp = timestampVal = new DateTime(2024, 06, 05, 0, 33, 1, 001, DateTimeKind.Utc).ToTimestampMS(); + if (snapshot != null) + header.Index = indexVal = NativeContract.Ledger.CurrentIndex(snapshot) + 1; + else + header.Index = indexVal = 0; + header.Nonce = nonceVal = 0; + header.NextConsensus = val160 = UInt160.Zero; + header.Witness = scriptVal = new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = new[] { (byte)OpCode.PUSH1 } + }; + } - transactionsVal = new Transaction[numberOfTransactions]; - if (numberOfTransactions > 0) + public static void SetupBlockWithValues(DataCache snapshot, Block block, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out ulong nonceVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, int numberOfTransactions) { - for (int i = 0; i < numberOfTransactions; i++) + Header header = new Header(); + SetupHeaderWithValues(snapshot, header, val256, out merkRootVal, out val160, out timestampVal, out nonceVal, out indexVal, out scriptVal); + + transactionsVal = new Transaction[numberOfTransactions]; + if (numberOfTransactions > 0) { - transactionsVal[i] = GetTransaction(UInt160.Zero); + for (int i = 0; i < numberOfTransactions; i++) + { + transactionsVal[i] = GetTransaction(UInt160.Zero); + } } - } - - block.Header = header; - block.Transactions = transactionsVal; - header.MerkleRoot = merkRootVal = MerkleTree.ComputeRoot(block.Transactions.Select(p => p.Hash).ToArray()); - } + block.Header = header; + block.Transactions = transactionsVal; - public static Block CreateBlockWithValidTransactions(DataCache snapshot, NEP6Wallet wallet, WalletAccount account, int numberOfTransactions) - { - var transactions = new List(); - for (var i = 0; i < numberOfTransactions; i++) - { - transactions.Add(CreateValidTx(snapshot, wallet, account)); + header.MerkleRoot = merkRootVal = MerkleTree.ComputeRoot(block.Transactions.Select(p => p.Hash).ToArray()); } - return CreateBlockWithValidTransactions(snapshot, account, transactions.ToArray()); - } + public static Block CreateBlockWithValidTransactions(DataCache snapshot, NEP6Wallet wallet, WalletAccount account, int numberOfTransactions) + { + var transactions = new List(); + for (var i = 0; i < numberOfTransactions; i++) + { + transactions.Add(CreateValidTx(snapshot, wallet, account)); + } - public static Block CreateBlockWithValidTransactions(DataCache snapshot, WalletAccount account, Transaction[] transactions) - { - var block = new Block(); - var header = new Header(); - var state = snapshot.TryGet(NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)).GetInteroperable(); - SetupHeaderWithValues(snapshot, header, state.Hash, out _, out _, out _, out _, out _, out _); - - block.Header = header; - block.Transactions = transactions; - - header.MerkleRoot = MerkleTree.ComputeRoot(block.Transactions.Select(p => p.Hash).ToArray()); - var contract = Contract.CreateMultiSigContract(1, TestProtocolSettings.SoleNode.StandbyCommittee); - var sc = new ContractParametersContext(snapshot, header, TestProtocolSettings.SoleNode.Network); - var signature = header.Sign(account.GetKey(), TestProtocolSettings.SoleNode.Network); - sc.AddSignature(contract, TestProtocolSettings.SoleNode.StandbyCommittee[0], signature.ToArray()); - block.Header.Witness = sc.GetWitnesses()[0]; - - return block; - } + return CreateBlockWithValidTransactions(snapshot, account, transactions.ToArray()); + } - public static void BlocksDelete(DataCache snapshot, UInt256 hash) - { - snapshot.Delete(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, hash)); - snapshot.Delete(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash)); - } + public static Block CreateBlockWithValidTransactions(DataCache snapshot, WalletAccount account, Transaction[] transactions) + { + var block = new Block(); + var header = new Header(); + var state = snapshot.TryGet(NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)).GetInteroperable(); + SetupHeaderWithValues(snapshot, header, state.Hash, out _, out _, out _, out _, out _, out _); + + block.Header = header; + block.Transactions = transactions; + + header.MerkleRoot = MerkleTree.ComputeRoot(block.Transactions.Select(p => p.Hash).ToArray()); + var contract = Contract.CreateMultiSigContract(1, TestProtocolSettings.SoleNode.StandbyCommittee); + var sc = new ContractParametersContext(snapshot, header, TestProtocolSettings.SoleNode.Network); + var signature = header.Sign(account.GetKey(), TestProtocolSettings.SoleNode.Network); + sc.AddSignature(contract, TestProtocolSettings.SoleNode.StandbyCommittee[0], signature.ToArray()); + block.Header.Witness = sc.GetWitnesses()[0]; + + return block; + } - public static void TransactionAdd(DataCache snapshot, params TransactionState[] txs) - { - foreach (var tx in txs) + public static void BlocksDelete(DataCache snapshot, UInt256 hash) { - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Transaction.Hash), new StorageItem(tx)); + snapshot.Delete(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, hash)); + snapshot.Delete(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash)); } - } - public static void BlocksAdd(DataCache snapshot, UInt256 hash, TrimmedBlock block) - { - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray())); - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToArray())); + public static void TransactionAdd(DataCache snapshot, params TransactionState[] txs) + { + foreach (var tx in txs) + { + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Transaction.Hash), new StorageItem(tx)); + } + } - var state = snapshot.GetAndChange(NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock), () => new StorageItem(new HashIndexState())).GetInteroperable(); - state.Hash = hash; - state.Index = block.Index; - } + public static void BlocksAdd(DataCache snapshot, UInt256 hash, TrimmedBlock block) + { + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray())); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToArray())); - public static void BlocksAdd(DataCache snapshot, UInt256 hash, Block block) - { + var state = snapshot.GetAndChange(NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock), () => new StorageItem(new HashIndexState())).GetInteroperable(); + state.Hash = hash; + state.Index = block.Index; + } - block.Transactions.ForEach(tx => + public static void BlocksAdd(DataCache snapshot, UInt256 hash, Block block) { - var state = new TransactionState + + block.Transactions.ForEach(tx => { - BlockIndex = block.Index, - Transaction = tx - }; - TransactionAdd(snapshot, state); - }); - - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray())); - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToTrimmedBlock().ToArray())); - var state = snapshot.GetAndChange(NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock), () => new StorageItem(new HashIndexState())).GetInteroperable(); - state.Hash = hash; - state.Index = block.Index; - } + var state = new TransactionState + { + BlockIndex = block.Index, + Transaction = tx + }; + TransactionAdd(snapshot, state); + }); + + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray())); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToTrimmedBlock().ToArray())); + var state = snapshot.GetAndChange(NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock), () => new StorageItem(new HashIndexState())).GetInteroperable(); + state.Hash = hash; + state.Index = block.Index; + } - public static string CreateInvalidBlockFormat() - { - // Create a valid block - var validBlock = new Block + public static string CreateInvalidBlockFormat() { - Header = new Header + // Create a valid block + var validBlock = new Block { - Version = 0, - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - Timestamp = 0, - Index = 0, - NextConsensus = UInt160.Zero, - Witness = new Witness { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } - }, - Transactions = [] - }; - - // Serialize the valid block - byte[] validBlockBytes = validBlock.ToArray(); - - // Corrupt the serialized data - // For example, we can truncate the data by removing the last few bytes - byte[] invalidBlockBytes = new byte[validBlockBytes.Length - 5]; - Array.Copy(validBlockBytes, invalidBlockBytes, invalidBlockBytes.Length); - - // Convert the corrupted data to a Base64 string - return Convert.ToBase64String(invalidBlockBytes); - } + Header = new Header + { + Version = 0, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + Timestamp = 0, + Index = 0, + NextConsensus = UInt160.Zero, + Witness = new Witness { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } + }, + Transactions = [] + }; - public static TrimmedBlock ToTrimmedBlock(this Block block) - { - return new TrimmedBlock + // Serialize the valid block + byte[] validBlockBytes = validBlock.ToArray(); + + // Corrupt the serialized data + // For example, we can truncate the data by removing the last few bytes + byte[] invalidBlockBytes = new byte[validBlockBytes.Length - 5]; + Array.Copy(validBlockBytes, invalidBlockBytes, invalidBlockBytes.Length); + + // Convert the corrupted data to a Base64 string + return Convert.ToBase64String(invalidBlockBytes); + } + + public static TrimmedBlock ToTrimmedBlock(this Block block) { - Header = block.Header, - Hashes = block.Transactions.Select(p => p.Hash).ToArray() - }; + return new TrimmedBlock + { + Header = block.Header, + Hashes = block.Transactions.Select(p => p.Hash).ToArray() + }; + } } } diff --git a/tests/Neo.UnitTests/TestUtils.Contract.cs b/tests/Neo.UnitTests/TestUtils.Contract.cs index da3139561c..1b184adcc6 100644 --- a/tests/Neo.UnitTests/TestUtils.Contract.cs +++ b/tests/Neo.UnitTests/TestUtils.Contract.cs @@ -14,92 +14,93 @@ using System; using System.Linq; -namespace Neo.UnitTests; - -partial class TestUtils +namespace Neo.UnitTests { - public static ContractManifest CreateDefaultManifest() + partial class TestUtils { - return new ContractManifest + public static ContractManifest CreateDefaultManifest() { - Name = "testManifest", - Groups = [], - SupportedStandards = [], - Abi = new ContractAbi + return new ContractManifest { - Events = [], - Methods = - [ - new ContractMethodDescriptor - { - Name = "testMethod", - Parameters = [], - ReturnType = ContractParameterType.Void, - Offset = 0, - Safe = true - } - ] - }, - Permissions = [ContractPermission.DefaultPermission], - Trusts = WildcardContainer.Create(), - Extra = null - }; - } - - public static ContractManifest CreateManifest(string method, ContractParameterType returnType, params ContractParameterType[] parameterTypes) - { - var manifest = CreateDefaultManifest(); - manifest.Abi.Methods = - [ - new ContractMethodDescriptor() - { - Name = method, - Parameters = parameterTypes.Select((p, i) => new ContractParameterDefinition + Name = "testManifest", + Groups = [], + SupportedStandards = [], + Abi = new ContractAbi { - Name = $"p{i}", - Type = p - }).ToArray(), - ReturnType = returnType - } - ]; - return manifest; - } + Events = [], + Methods = + [ + new ContractMethodDescriptor + { + Name = "testMethod", + Parameters = [], + ReturnType = ContractParameterType.Void, + Offset = 0, + Safe = true + } + ] + }, + Permissions = [ContractPermission.DefaultPermission], + Trusts = WildcardContainer.Create(), + Extra = null + }; + } - public static ContractState GetContract(string method = "test", int parametersCount = 0) - { - NefFile nef = new() - { - Compiler = "", - Source = "", - Tokens = [], - Script = new byte[] { 0x01, 0x01, 0x01, 0x01 } - }; - nef.CheckSum = NefFile.ComputeChecksum(nef); - return new ContractState + public static ContractManifest CreateManifest(string method, ContractParameterType returnType, params ContractParameterType[] parameterTypes) { - Id = 0x43000000, - Nef = nef, - Hash = nef.Script.Span.ToScriptHash(), - Manifest = CreateManifest(method, ContractParameterType.Any, Enumerable.Repeat(ContractParameterType.Any, parametersCount).ToArray()) - }; - } + var manifest = CreateDefaultManifest(); + manifest.Abi.Methods = + [ + new ContractMethodDescriptor() + { + Name = method, + Parameters = parameterTypes.Select((p, i) => new ContractParameterDefinition + { + Name = $"p{i}", + Type = p + }).ToArray(), + ReturnType = returnType + } + ]; + return manifest; + } - internal static ContractState GetContract(byte[] script, ContractManifest manifest = null) - { - NefFile nef = new() + public static ContractState GetContract(string method = "test", int parametersCount = 0) { - Compiler = "", - Source = "", - Tokens = [], - Script = script - }; - nef.CheckSum = NefFile.ComputeChecksum(nef); - return new ContractState + NefFile nef = new() + { + Compiler = "", + Source = "", + Tokens = [], + Script = new byte[] { 0x01, 0x01, 0x01, 0x01 } + }; + nef.CheckSum = NefFile.ComputeChecksum(nef); + return new ContractState + { + Id = 0x43000000, + Nef = nef, + Hash = nef.Script.Span.ToScriptHash(), + Manifest = CreateManifest(method, ContractParameterType.Any, Enumerable.Repeat(ContractParameterType.Any, parametersCount).ToArray()) + }; + } + + internal static ContractState GetContract(byte[] script, ContractManifest manifest = null) { - Id = 1, - Hash = script.ToScriptHash(), - Nef = nef, - Manifest = manifest ?? CreateDefaultManifest() - }; + NefFile nef = new() + { + Compiler = "", + Source = "", + Tokens = [], + Script = script + }; + nef.CheckSum = NefFile.ComputeChecksum(nef); + return new ContractState + { + Id = 1, + Hash = script.ToScriptHash(), + Nef = nef, + Manifest = manifest ?? CreateDefaultManifest() + }; + } } } diff --git a/tests/Neo.UnitTests/TestUtils.Transaction.cs b/tests/Neo.UnitTests/TestUtils.Transaction.cs index 70ac264a19..116551fc57 100644 --- a/tests/Neo.UnitTests/TestUtils.Transaction.cs +++ b/tests/Neo.UnitTests/TestUtils.Transaction.cs @@ -26,207 +26,208 @@ using System.Linq; using System.Numerics; -namespace Neo.UnitTests; - -public partial class TestUtils +namespace Neo.UnitTests { - public static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, WalletAccount account) - { - return CreateValidTx(snapshot, wallet, account.ScriptHash, (uint)new Random().Next()); - } - - public static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce) + public partial class TestUtils { - var tx = wallet.MakeTransaction(snapshot, [ - new TransferOutput - { - AssetId = NativeContract.GAS.Hash, - ScriptHash = account, - Value = new BigDecimal(BigInteger.One, 8) - } - ], - account); - - tx.Nonce = nonce; - tx.Signers = [new Signer { Account = account, Scopes = WitnessScope.CalledByEntry }]; - var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); - Assert.IsNull(data.GetSignatures(tx.Sender)); - Assert.IsTrue(wallet.Sign(data)); - Assert.IsTrue(data.Completed); - Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count); - - tx.Witnesses = data.GetWitnesses(); - return tx; - } - - public static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce, UInt256[] conflicts) - { - var tx = wallet.MakeTransaction(snapshot, [ - new TransferOutput - { - AssetId = NativeContract.GAS.Hash, - ScriptHash = account, - Value = new BigDecimal(BigInteger.One, 8) - } - ], - account); - tx.Attributes = conflicts.Select(conflict => new Conflicts { Hash = conflict }).ToArray(); - tx.Nonce = nonce; - tx.Signers = [new Signer { Account = account, Scopes = WitnessScope.CalledByEntry }]; - var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); - Assert.IsNull(data.GetSignatures(tx.Sender)); - Assert.IsTrue(wallet.Sign(data)); - Assert.IsTrue(data.Completed); - Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count); - tx.Witnesses = data.GetWitnesses(); - return tx; - } - - public static Transaction CreateRandomHashTransaction() - { - var randomBytes = new byte[16]; - TestRandom.NextBytes(randomBytes); - return new Transaction + public static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, WalletAccount account) { - Script = randomBytes, - Attributes = [], - Signers = [new Signer { Account = UInt160.Zero }], - Witnesses = - [ - new Witness - { - InvocationScript = Array.Empty(), - VerificationScript = Array.Empty() - } - ] - }; - } + return CreateValidTx(snapshot, wallet, account.ScriptHash, (uint)new Random().Next()); + } - public static Transaction GetTransaction(UInt160 sender) - { - return new Transaction + public static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce) { - Script = new[] { (byte)OpCode.PUSH2 }, - Attributes = [], - Signers = - [ - new Signer - { - Account = sender, - Scopes = WitnessScope.CalledByEntry, - AllowedContracts = [], - AllowedGroups = [], - Rules = [], - } - ], - Witnesses = - [ - new Witness - { - InvocationScript = Array.Empty(), - VerificationScript = Array.Empty() - } - ] - }; - } - - public static Transaction CreateInvalidTransaction(DataCache snapshot, NEP6Wallet wallet, WalletAccount account, InvalidTransactionType type, UInt256 conflict = null) - { - var rand = new Random(); - var sender = account.ScriptHash; + var tx = wallet.MakeTransaction(snapshot, [ + new TransferOutput + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account, + Value = new BigDecimal(BigInteger.One, 8) + } + ], + account); + + tx.Nonce = nonce; + tx.Signers = [new Signer { Account = account, Scopes = WitnessScope.CalledByEntry }]; + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); + Assert.IsNull(data.GetSignatures(tx.Sender)); + Assert.IsTrue(wallet.Sign(data)); + Assert.IsTrue(data.Completed); + Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count); + + tx.Witnesses = data.GetWitnesses(); + return tx; + } - var tx = new Transaction + public static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce, UInt256[] conflicts) { - Version = 0, - Nonce = (uint)rand.Next(), - ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + wallet.ProtocolSettings.MaxValidUntilBlockIncrement, - Signers = [new Signer { Account = sender, Scopes = WitnessScope.CalledByEntry }], - Attributes = [], - Script = new[] { (byte)OpCode.RET } - }; - - switch (type) + var tx = wallet.MakeTransaction(snapshot, [ + new TransferOutput + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account, + Value = new BigDecimal(BigInteger.One, 8) + } + ], + account); + tx.Attributes = conflicts.Select(conflict => new Conflicts { Hash = conflict }).ToArray(); + tx.Nonce = nonce; + tx.Signers = [new Signer { Account = account, Scopes = WitnessScope.CalledByEntry }]; + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); + Assert.IsNull(data.GetSignatures(tx.Sender)); + Assert.IsTrue(wallet.Sign(data)); + Assert.IsTrue(data.Completed); + Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count); + tx.Witnesses = data.GetWitnesses(); + return tx; + } + + public static Transaction CreateRandomHashTransaction() { - case InvalidTransactionType.InsufficientBalance: - // Set an unrealistically high system fee - tx.SystemFee = long.MaxValue; - break; - case InvalidTransactionType.InvalidScript: - // Use an invalid script - tx.Script = new byte[] { 0xFF }; - break; - case InvalidTransactionType.InvalidAttribute: - // Add an invalid attribute - tx.Attributes = [new InvalidAttribute()]; - break; - case InvalidTransactionType.Oversized: - // Make the transaction oversized - tx.Script = new byte[Transaction.MaxTransactionSize]; - break; - case InvalidTransactionType.Expired: - // Set an expired ValidUntilBlock - tx.ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) - 1; - break; - case InvalidTransactionType.Conflicting: - // To create a conflicting transaction, we'd need another valid transaction. - // For simplicity, we'll just add a Conflicts attribute with a random hash. - tx.Attributes = [new Conflicts { Hash = conflict }]; - break; + var randomBytes = new byte[16]; + TestRandom.NextBytes(randomBytes); + return new Transaction + { + Script = randomBytes, + Attributes = [], + Signers = [new Signer { Account = UInt160.Zero }], + Witnesses = + [ + new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = Array.Empty() + } + ] + }; } - var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); - Assert.IsNull(data.GetSignatures(tx.Sender)); - Assert.IsTrue(wallet.Sign(data)); - Assert.IsTrue(data.Completed); - Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count); - tx.Witnesses = data.GetWitnesses(); - if (type == InvalidTransactionType.InvalidSignature) + public static Transaction GetTransaction(UInt160 sender) { - tx.Witnesses[0] = new Witness + return new Transaction { - InvocationScript = new byte[] { (byte)OpCode.PUSHDATA1, 64 }.Concat(new byte[64]).ToArray(), - VerificationScript = data.GetWitnesses()[0].VerificationScript + Script = new[] { (byte)OpCode.PUSH2 }, + Attributes = [], + Signers = + [ + new Signer + { + Account = sender, + Scopes = WitnessScope.CalledByEntry, + AllowedContracts = [], + AllowedGroups = [], + Rules = [], + } + ], + Witnesses = + [ + new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = Array.Empty() + } + ] }; } - return tx; - } + public static Transaction CreateInvalidTransaction(DataCache snapshot, NEP6Wallet wallet, WalletAccount account, InvalidTransactionType type, UInt256 conflict = null) + { + var rand = new Random(); + var sender = account.ScriptHash; - public enum InvalidTransactionType - { - InsufficientBalance, - InvalidSignature, - InvalidScript, - InvalidAttribute, - Oversized, - Expired, - Conflicting - } + var tx = new Transaction + { + Version = 0, + Nonce = (uint)rand.Next(), + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + wallet.ProtocolSettings.MaxValidUntilBlockIncrement, + Signers = [new Signer { Account = sender, Scopes = WitnessScope.CalledByEntry }], + Attributes = [], + Script = new[] { (byte)OpCode.RET } + }; - class InvalidAttribute : TransactionAttribute - { - public override TransactionAttributeType Type => (TransactionAttributeType)0xFF; - public override bool AllowMultiple { get; } - protected override void DeserializeWithoutType(ref MemoryReader reader) { } - protected override void SerializeWithoutType(BinaryWriter writer) { } - } + switch (type) + { + case InvalidTransactionType.InsufficientBalance: + // Set an unrealistically high system fee + tx.SystemFee = long.MaxValue; + break; + case InvalidTransactionType.InvalidScript: + // Use an invalid script + tx.Script = new byte[] { 0xFF }; + break; + case InvalidTransactionType.InvalidAttribute: + // Add an invalid attribute + tx.Attributes = [new InvalidAttribute()]; + break; + case InvalidTransactionType.Oversized: + // Make the transaction oversized + tx.Script = new byte[Transaction.MaxTransactionSize]; + break; + case InvalidTransactionType.Expired: + // Set an expired ValidUntilBlock + tx.ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) - 1; + break; + case InvalidTransactionType.Conflicting: + // To create a conflicting transaction, we'd need another valid transaction. + // For simplicity, we'll just add a Conflicts attribute with a random hash. + tx.Attributes = [new Conflicts { Hash = conflict }]; + break; + } + + var data = new ContractParametersContext(snapshot, tx, TestProtocolSettings.Default.Network); + Assert.IsNull(data.GetSignatures(tx.Sender)); + Assert.IsTrue(wallet.Sign(data)); + Assert.IsTrue(data.Completed); + Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count); + tx.Witnesses = data.GetWitnesses(); + if (type == InvalidTransactionType.InvalidSignature) + { + tx.Witnesses[0] = new Witness + { + InvocationScript = new byte[] { (byte)OpCode.PUSHDATA1, 64 }.Concat(new byte[64]).ToArray(), + VerificationScript = data.GetWitnesses()[0].VerificationScript + }; + } - public static void AddTransactionToBlockchain(DataCache snapshot, Transaction tx) - { - var block = new Block + return tx; + } + + public enum InvalidTransactionType + { + InsufficientBalance, + InvalidSignature, + InvalidScript, + InvalidAttribute, + Oversized, + Expired, + Conflicting + } + + class InvalidAttribute : TransactionAttribute + { + public override TransactionAttributeType Type => (TransactionAttributeType)0xFF; + public override bool AllowMultiple { get; } + protected override void DeserializeWithoutType(ref MemoryReader reader) { } + protected override void SerializeWithoutType(BinaryWriter writer) { } + } + + public static void AddTransactionToBlockchain(DataCache snapshot, Transaction tx) { - Header = new Header + var block = new Block { - Index = NativeContract.Ledger.CurrentIndex(snapshot) + 1, - PrevHash = NativeContract.Ledger.CurrentHash(snapshot), - MerkleRoot = new UInt256(Crypto.Hash256(tx.Hash.ToArray())), - Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS(), - NextConsensus = UInt160.Zero, - Witness = new Witness { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } - }, - Transactions = [tx] - }; - - BlocksAdd(snapshot, block.Hash, block); + Header = new Header + { + Index = NativeContract.Ledger.CurrentIndex(snapshot) + 1, + PrevHash = NativeContract.Ledger.CurrentHash(snapshot), + MerkleRoot = new UInt256(Crypto.Hash256(tx.Hash.ToArray())), + Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS(), + NextConsensus = UInt160.Zero, + Witness = new Witness { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } + }, + Transactions = [tx] + }; + + BlocksAdd(snapshot, block.Hash, block); + } } } diff --git a/tests/Neo.UnitTests/TestUtils.cs b/tests/Neo.UnitTests/TestUtils.cs index a9ad4d8ff7..3b768940e8 100644 --- a/tests/Neo.UnitTests/TestUtils.cs +++ b/tests/Neo.UnitTests/TestUtils.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.Network.P2P.Payloads; diff --git a/tests/Neo.UnitTests/UT_Helper.cs b/tests/Neo.UnitTests/UT_Helper.cs index 6cf1605a24..2447d5e251 100644 --- a/tests/Neo.UnitTests/UT_Helper.cs +++ b/tests/Neo.UnitTests/UT_Helper.cs @@ -77,5 +77,77 @@ public void TestRemoveHashsetHashSetCache() CollectionAssert.AreEqual(new int[] { 3 }, a.ToArray()); } + + [TestMethod] + public void TestToHexString() + { + byte[] nullStr = null; + Assert.ThrowsException(() => nullStr.ToHexString()); + byte[] empty = Array.Empty(); + empty.ToHexString().Should().Be(""); + empty.ToHexString(false).Should().Be(""); + empty.ToHexString(true).Should().Be(""); + + byte[] str1 = new byte[] { (byte)'n', (byte)'e', (byte)'o' }; + str1.ToHexString().Should().Be("6e656f"); + str1.ToHexString(false).Should().Be("6e656f"); + str1.ToHexString(true).Should().Be("6f656e"); + } + + [TestMethod] + public void TestGetVersion() + { + // assembly without version + + var asm = AppDomain.CurrentDomain.GetAssemblies() + .Where(u => u.FullName == "Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") + .FirstOrDefault(); + string version = asm?.GetVersion() ?? ""; + version.Should().Be("0.0.0"); + } + + [TestMethod] + public void TestToByteArrayStandard() + { + BigInteger number = BigInteger.Zero; + Assert.AreEqual("", number.ToByteArrayStandard().ToHexString()); + + number = BigInteger.One; + Assert.AreEqual("01", number.ToByteArrayStandard().ToHexString()); + } + + [TestMethod] + public void TestNextBigIntegerForRandom() + { + Random ran = new(); + Action action1 = () => ran.NextBigInteger(-1); + action1.Should().Throw(); + + ran.NextBigInteger(0).Should().Be(0); + ran.NextBigInteger(8).Should().NotBeNull(); + ran.NextBigInteger(9).Should().NotBeNull(); + } + + [TestMethod] + public void TestUnmapForIPAddress() + { + var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); + addr.UnMap().Should().Be(addr); + + var addr2 = addr.MapToIPv6(); + addr2.UnMap().Should().Be(addr); + } + + [TestMethod] + public void TestUnmapForIPEndPoin() + { + var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); + var endPoint = new IPEndPoint(addr, 8888); + endPoint.UnMap().Should().Be(endPoint); + + var addr2 = addr.MapToIPv6(); + var endPoint2 = new IPEndPoint(addr2, 8888); + endPoint2.UnMap().Should().Be(endPoint); + } } } diff --git a/tests/Neo.UnitTests/UT_UInt160.cs b/tests/Neo.UnitTests/UT_UInt160.cs index 3502c7cf90..3fb9dd89a0 100644 --- a/tests/Neo.UnitTests/UT_UInt160.cs +++ b/tests/Neo.UnitTests/UT_UInt160.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using System; using System.Security.Cryptography; @@ -126,5 +127,18 @@ public void TestOperatorSmallerAndEqual() Assert.AreEqual(true, UInt160.Zero <= UInt160.Zero); Assert.IsTrue(UInt160.Zero >= "0x0000000000000000000000000000000000000000"); } + + [TestMethod] + public void TestSpanAndSerialize() + { + // random data + var random = new Random(); + var data = new byte[UInt160.Length]; + random.NextBytes(data); + + var value = new UInt160(data); + var span = value.GetSpan(); + Assert.IsTrue(span.SequenceEqual(value.ToArray())); + } } } diff --git a/tests/Neo.UnitTests/UT_UInt256.cs b/tests/Neo.UnitTests/UT_UInt256.cs index b2bd02dac3..40006b4b67 100644 --- a/tests/Neo.UnitTests/UT_UInt256.cs +++ b/tests/Neo.UnitTests/UT_UInt256.cs @@ -13,6 +13,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Extensions; using Neo.IO; using System; using System.IO; @@ -155,5 +156,17 @@ public void TestOperatorSmallerAndEqual() { Assert.AreEqual(true, UInt256.Zero <= UInt256.Zero); } + + [TestMethod] + public void TestSpanAndSerialize() + { + var random = new Random(); + var data = new byte[UInt256.Length]; + random.NextBytes(data); + + var value = new UInt256(data); + var span = value.GetSpan(); + Assert.IsTrue(span.SequenceEqual(value.ToArray())); + } } } diff --git a/tests/Neo.UnitTests/VM/UT_Helper.cs b/tests/Neo.UnitTests/VM/UT_Helper.cs index cabe0c7a02..cd7b8f01bb 100644 --- a/tests/Neo.UnitTests/VM/UT_Helper.cs +++ b/tests/Neo.UnitTests/VM/UT_Helper.cs @@ -452,7 +452,7 @@ private void TestEmitPush3Ulong() { ScriptBuilder sb = new ScriptBuilder(); ulong temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -462,7 +462,7 @@ private void TestEmitPush3Long() { ScriptBuilder sb = new ScriptBuilder(); long temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -472,7 +472,7 @@ private void TestEmitPush3Uint() { ScriptBuilder sb = new ScriptBuilder(); uint temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -482,7 +482,7 @@ private void TestEmitPush3Int() { ScriptBuilder sb = new ScriptBuilder(); int temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -492,7 +492,7 @@ private void TestEmitPush3Ushort() { ScriptBuilder sb = new ScriptBuilder(); ushort temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -502,7 +502,7 @@ private void TestEmitPush3Char() { ScriptBuilder sb = new ScriptBuilder(); char temp = char.MinValue; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -512,7 +512,7 @@ private void TestEmitPush3Short() { ScriptBuilder sb = new ScriptBuilder(); short temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -522,7 +522,7 @@ private void TestEmitPush3Byte() { ScriptBuilder sb = new ScriptBuilder(); byte temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -532,7 +532,7 @@ private void TestEmitPush3Sbyte() { ScriptBuilder sb = new ScriptBuilder(); sbyte temp = 0; - VM.Helper.EmitPush(sb, temp); + sb.EmitPush(temp); byte[] tempArray = new byte[1]; tempArray[0] = (byte)OpCode.PUSH0; CollectionAssert.AreEqual(tempArray, sb.ToArray()); diff --git a/tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs index 843eeae192..055ea193f7 100644 --- a/tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/tests/Neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.Extensions; using Neo.IO; using Neo.Wallets; using System; diff --git a/tests/Neo.VM.Tests/Neo.VM.Tests.csproj b/tests/Neo.VM.Tests/Neo.VM.Tests.csproj index 2766ce05d2..b1de7bc3cc 100644 --- a/tests/Neo.VM.Tests/Neo.VM.Tests.csproj +++ b/tests/Neo.VM.Tests/Neo.VM.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 Neo.Test true @@ -17,9 +17,9 @@ - - - + + + diff --git a/tests/Neo.VM.Tests/UT_ScriptBuilder.cs b/tests/Neo.VM.Tests/UT_ScriptBuilder.cs index 6d5b4e6f2d..5903cecba8 100644 --- a/tests/Neo.VM.Tests/UT_ScriptBuilder.cs +++ b/tests/Neo.VM.Tests/UT_ScriptBuilder.cs @@ -42,6 +42,21 @@ public void TestEmit() } } + [TestMethod] + public void TestNullAndEmpty() + { + using (ScriptBuilder script = new()) + { + ReadOnlySpan span = null; + script.EmitPush(span); + + span = []; + script.EmitPush(span); + + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA1, 0, (byte)OpCode.PUSHDATA1, 0 }, script.ToArray()); + } + } + [TestMethod] public void TestBigInteger() { @@ -233,7 +248,8 @@ public void TestEmitPushByteArray() { using (ScriptBuilder script = new()) { - Assert.ThrowsException(() => script.EmitPush((byte[])null)); + script.EmitPush((byte[])null); + CollectionAssert.AreEqual(new byte[] { (byte)OpCode.PUSHDATA1, 0 }, script.ToArray()); } using (ScriptBuilder script = new()) diff --git a/tests/Neo.VM.Tests/UT_Unsafe.cs b/tests/Neo.VM.Tests/UT_Unsafe.cs index e3b4b8708f..f42903c3cc 100644 --- a/tests/Neo.VM.Tests/UT_Unsafe.cs +++ b/tests/Neo.VM.Tests/UT_Unsafe.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.VM; +using System; namespace Neo.Test { @@ -20,14 +21,32 @@ public class UT_Unsafe [TestMethod] public void TestNotZero() { - Assert.IsFalse(Unsafe.NotZero(System.Array.Empty())); - Assert.IsFalse(Unsafe.NotZero(new byte[4])); - Assert.IsFalse(Unsafe.NotZero(new byte[8])); - Assert.IsFalse(Unsafe.NotZero(new byte[11])); + Assert.IsFalse(new ReadOnlySpan(System.Array.Empty()).NotZero()); + Assert.IsFalse(new ReadOnlySpan(new byte[4]).NotZero()); + Assert.IsFalse(new ReadOnlySpan(new byte[7]).NotZero()); + Assert.IsFalse(new ReadOnlySpan(new byte[8]).NotZero()); + Assert.IsFalse(new ReadOnlySpan(new byte[9]).NotZero()); + Assert.IsFalse(new ReadOnlySpan(new byte[11]).NotZero()); - Assert.IsTrue(Unsafe.NotZero(new byte[4] { 0x00, 0x00, 0x00, 0x01 })); - Assert.IsTrue(Unsafe.NotZero(new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 })); - Assert.IsTrue(Unsafe.NotZero(new byte[11] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 })); + Assert.IsTrue(new ReadOnlySpan(new byte[4] { 0x00, 0x00, 0x00, 0x01 }).NotZero()); + Assert.IsTrue(new ReadOnlySpan(new byte[7] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }).NotZero()); + Assert.IsTrue(new ReadOnlySpan(new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }).NotZero()); + Assert.IsTrue(new ReadOnlySpan(new byte[9] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }).NotZero()); + Assert.IsTrue(new ReadOnlySpan(new byte[11] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }).NotZero()); + + var bytes = new byte[64]; + for (int i = 0; i < bytes.Length; i++) + { + ReadOnlySpan span = bytes.AsSpan(); + Assert.IsFalse(span[i..].NotZero()); + + for (int j = i; j < bytes.Length; j++) + { + bytes[j] = 0x01; + Assert.IsTrue(span[i..].NotZero()); + bytes[j] = 0x00; + } + } } } } diff --git a/tests/Neo.VM.Tests/UT_Utility.cs b/tests/Neo.VM.Tests/UT_Utility.cs index e87bcbcda7..8806050900 100644 --- a/tests/Neo.VM.Tests/UT_Utility.cs +++ b/tests/Neo.VM.Tests/UT_Utility.cs @@ -54,7 +54,7 @@ public void TestGetBitLength() var random = new Random(); // Big Number (net standard didn't work) - VerifyGetBitLength(BigInteger.One << 32 << int.MaxValue, 2147483680); + Assert.ThrowsException(() => VerifyGetBitLength(BigInteger.One << 32 << int.MaxValue, 2147483680)); // Trivial cases // sign bit|shortest two's complement