From a38d618ed8306d27b769fb0c38e4b9f54a567344 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 4 Oct 2024 06:00:00 -0400 Subject: [PATCH 01/53] Added `CreateStruct` & Fixed `CreateMap` (#3494) * Added `CreateStruct`, Fixed CreateMap * Added test and comments --------- Co-authored-by: Jimmy Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> Co-authored-by: Shargon --- src/Neo/VM/Helper.cs | 70 ++++++++++++++++++++++++----- tests/Neo.UnitTests/VM/UT_Helper.cs | 24 ++++++++++ 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/Neo/VM/Helper.cs b/src/Neo/VM/Helper.cs index c6423ac1f1..26d0fa132a 100644 --- a/src/Neo/VM/Helper.cs +++ b/src/Neo/VM/Helper.cs @@ -54,18 +54,64 @@ public static ScriptBuilder CreateArray(this ScriptBuilder builder, IReadOnly /// The to be used. /// The key/value pairs of the map. /// The same instance as . - public static ScriptBuilder CreateMap(this ScriptBuilder builder, IEnumerable> map = null) + public static ScriptBuilder CreateMap(this ScriptBuilder builder, IEnumerable> map) + where TKey : notnull + where TValue : notnull { - builder.Emit(OpCode.NEWMAP); - if (map != null) - foreach (var p in map) - { - builder.Emit(OpCode.DUP); - builder.EmitPush(p.Key); - builder.EmitPush(p.Value); - builder.Emit(OpCode.SETITEM); - } - return builder; + 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); } /// @@ -218,7 +264,7 @@ public static ScriptBuilder EmitPush(this ScriptBuilder builder, object obj) builder.EmitPush(data); break; case char data: - builder.EmitPush((ushort)data); + builder.EmitPush(data); break; case ushort data: builder.EmitPush(data); diff --git a/tests/Neo.UnitTests/VM/UT_Helper.cs b/tests/Neo.UnitTests/VM/UT_Helper.cs index 3e83256669..db47555dbd 100644 --- a/tests/Neo.UnitTests/VM/UT_Helper.cs +++ b/tests/Neo.UnitTests/VM/UT_Helper.cs @@ -106,6 +106,30 @@ public void TestEmitArray() Assert.AreEqual(0, engine2.ResultStack.Pop().Count); } + [TestMethod] + public void TestEmitStruct() + { + var expected = new BigInteger[] { 1, 2, 3 }; + var sb = new ScriptBuilder(); + sb.CreateStruct(expected); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, null); + engine.LoadScript(sb.ToArray()); + Assert.AreEqual(VMState.HALT, engine.Execute()); + + CollectionAssert.AreEqual(expected, engine.ResultStack.Pop().Select(u => u.GetInteger()).ToArray()); + + expected = new BigInteger[] { }; + sb = new ScriptBuilder(); + sb.CreateStruct(expected); + + using var engine2 = ApplicationEngine.Create(TriggerType.Application, null, null); + engine2.LoadScript(sb.ToArray()); + Assert.AreEqual(VMState.HALT, engine2.Execute()); + + Assert.AreEqual(0, engine2.ResultStack.Pop().Count); + } + [TestMethod] public void TestEmitMap() { From bb32a794e6c610de2833fc8c07edea1659bf40a0 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 7 Oct 2024 21:37:55 +0800 Subject: [PATCH 02/53] benchmark convert (#3509) --- benchmarks/Neo.VM.Benchmarks/Program.cs | 2 +- .../VMTypes/Benchmarks_Convert.cs | 217 ++++++++++++++++++ .../Benchmarks_DeepCopy.cs} | 4 +- 3 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs rename benchmarks/Neo.VM.Benchmarks/{Benchmarks.Types.cs => VMTypes/Benchmarks_DeepCopy.cs} (97%) diff --git a/benchmarks/Neo.VM.Benchmarks/Program.cs b/benchmarks/Neo.VM.Benchmarks/Program.cs index 028dff9460..29b4549674 100644 --- a/benchmarks/Neo.VM.Benchmarks/Program.cs +++ b/benchmarks/Neo.VM.Benchmarks/Program.cs @@ -12,4 +12,4 @@ using BenchmarkDotNet.Running; using Neo.VM.Benchmark; -BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs new file mode 100644 index 0000000000..0860749955 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs @@ -0,0 +1,217 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Benchmarks_Convert.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 Neo.VM.Types; +using System.Collections.Generic; +using Array = Neo.VM.Types.Array; +using Buffer = Neo.VM.Types.Buffer; + +namespace Neo.VM.Benchmark; + +public class Benchmarks_Convert +{ + private Dictionary> testItemsByType; + + [GlobalSetup] + public void Setup() + { + testItemsByType = CreateTestItemsByType(); + } + + [Benchmark] + [ArgumentsSource(nameof(GetTypeConversionPairs))] + public void BenchConvertTo(StackItemType fromType, StackItemType toType) + { + foreach (var item in testItemsByType[fromType]) + { + 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) + { + 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))) + { + result[type] = new List(); + } + + result[StackItemType.Boolean].Add(StackItem.True); + result[StackItemType.Boolean].Add(StackItem.False); + + result[StackItemType.Integer].Add(new Integer(42)); + result[StackItemType.Integer].Add(new Integer(-1)); + + result[StackItemType.ByteString].Add(new ByteString(new byte[] { 1, 2, 3 })); + result[StackItemType.ByteString].Add(new ByteString(new byte[] { 255, 0, 128 })); + + // 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 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 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 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 }); + + result[StackItemType.InteropInterface].Add(new InteropInterface(new object())); + result[StackItemType.InteropInterface].Add(new InteropInterface("test string")); + + return result; + } +} + +// BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.4249/23H2/2023Update/SunValley3) +// Intel Core i9-14900HX, 1 CPU, 32 logical and 24 physical cores +// .NET SDK 8.0.205 +// [Host] : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2 +// DefaultJob : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2 +// +// +// | Method | fromType | toType | Mean | Error | StdDev | +// |--------------- |----------------- |----------------- |-------------:|------------:|------------:| +// | BenchConvertTo | Any | Any | 1.762 ns | 0.0195 ns | 0.0182 ns | +// | BenchConvertTo | Any | Pointer | 1.791 ns | 0.0196 ns | 0.0183 ns | +// | BenchConvertTo | Any | Boolean | 1.774 ns | 0.0245 ns | 0.0229 ns | +// | BenchConvertTo | Any | Integer | 1.781 ns | 0.0236 ns | 0.0220 ns | +// | BenchConvertTo | Any | ByteString | 1.767 ns | 0.0255 ns | 0.0226 ns | +// | BenchConvertTo | Any | Buffer | 1.774 ns | 0.0217 ns | 0.0203 ns | +// | BenchConvertTo | Any | Array | 1.770 ns | 0.0412 ns | 0.0385 ns | +// | BenchConvertTo | Any | Struct | 1.787 ns | 0.0227 ns | 0.0212 ns | +// | BenchConvertTo | Any | Map | 1.796 ns | 0.0292 ns | 0.0273 ns | +// | BenchConvertTo | Any | InteropInterface | 1.820 ns | 0.0549 ns | 0.0675 ns | +// | BenchConvertTo | Pointer | Any | 2.312 ns | 0.0210 ns | 0.0175 ns | +// | BenchConvertTo | Pointer | Pointer | 2.337 ns | 0.0157 ns | 0.0146 ns | +// | BenchConvertTo | Pointer | Boolean | 2.352 ns | 0.0190 ns | 0.0169 ns | +// | BenchConvertTo | Pointer | Integer | 2.334 ns | 0.0231 ns | 0.0216 ns | +// | BenchConvertTo | Pointer | ByteString | 2.317 ns | 0.0298 ns | 0.0279 ns | +// | BenchConvertTo | Pointer | Buffer | 2.329 ns | 0.0274 ns | 0.0256 ns | +// | BenchConvertTo | Pointer | Array | 2.338 ns | 0.0257 ns | 0.0241 ns | +// | BenchConvertTo | Pointer | Struct | 2.336 ns | 0.0318 ns | 0.0298 ns | +// | BenchConvertTo | Pointer | Map | 2.351 ns | 0.0676 ns | 0.0903 ns | +// | BenchConvertTo | Pointer | InteropInterface | 2.281 ns | 0.0133 ns | 0.0125 ns | +// | BenchConvertTo | Boolean | Any | 5,926.451 ns | 118.1195 ns | 136.0266 ns | +// | BenchConvertTo | Boolean | Pointer | 6,001.282 ns | 15.3048 ns | 12.7802 ns | +// | BenchConvertTo | Boolean | Boolean | 4.459 ns | 0.0151 ns | 0.0133 ns | +// | BenchConvertTo | Boolean | Integer | 14.104 ns | 0.1526 ns | 0.1428 ns | +// | BenchConvertTo | Boolean | ByteString | 11.650 ns | 0.0539 ns | 0.0450 ns | +// | BenchConvertTo | Boolean | Buffer | 26.106 ns | 0.1549 ns | 0.1449 ns | +// | BenchConvertTo | Boolean | Array | 5,813.116 ns | 28.1911 ns | 26.3700 ns | +// | BenchConvertTo | Boolean | Struct | 5,809.844 ns | 19.1249 ns | 15.9702 ns | +// | BenchConvertTo | Boolean | Map | 6,061.558 ns | 29.3991 ns | 27.4999 ns | +// | BenchConvertTo | Boolean | InteropInterface | 5,924.682 ns | 80.5533 ns | 75.3496 ns | +// | BenchConvertTo | Integer | Any | 5,240.903 ns | 41.0628 ns | 38.4102 ns | +// | BenchConvertTo | Integer | Pointer | 5,479.116 ns | 75.8232 ns | 70.9251 ns | +// | BenchConvertTo | Integer | Boolean | 5.981 ns | 0.0445 ns | 0.0416 ns | +// | BenchConvertTo | Integer | Integer | 4.277 ns | 0.0177 ns | 0.0166 ns | +// | BenchConvertTo | Integer | ByteString | 19.053 ns | 0.2125 ns | 0.1883 ns | +// | BenchConvertTo | Integer | Buffer | 32.782 ns | 0.1653 ns | 0.1380 ns | +// | BenchConvertTo | Integer | Array | 4,693.207 ns | 14.2446 ns | 12.6275 ns | +// | BenchConvertTo | Integer | Struct | 4,737.341 ns | 60.1813 ns | 56.2936 ns | +// | BenchConvertTo | Integer | Map | 4,808.431 ns | 23.5380 ns | 22.0174 ns | +// | BenchConvertTo | Integer | InteropInterface | 4,684.409 ns | 24.7033 ns | 21.8989 ns | +// | BenchConvertTo | ByteString | Any | 5,833.857 ns | 20.1553 ns | 18.8533 ns | +// | BenchConvertTo | ByteString | Pointer | 5,807.973 ns | 11.7754 ns | 10.4386 ns | +// | BenchConvertTo | ByteString | Boolean | 33.007 ns | 0.1574 ns | 0.1472 ns | +// | BenchConvertTo | ByteString | Integer | 23.622 ns | 0.0755 ns | 0.0669 ns | +// | BenchConvertTo | ByteString | ByteString | 4.288 ns | 0.0152 ns | 0.0142 ns | +// | BenchConvertTo | ByteString | Buffer | 24.881 ns | 0.0889 ns | 0.0788 ns | +// | BenchConvertTo | ByteString | Array | 6,030.813 ns | 19.9562 ns | 18.6670 ns | +// | BenchConvertTo | ByteString | Struct | 5,811.185 ns | 24.0781 ns | 22.5226 ns | +// | BenchConvertTo | ByteString | Map | 5,866.820 ns | 17.0315 ns | 15.0980 ns | +// | BenchConvertTo | ByteString | InteropInterface | 5,757.124 ns | 16.3184 ns | 14.4658 ns | +// | BenchConvertTo | Buffer | Any | 4,886.279 ns | 17.1370 ns | 14.3102 ns | +// | BenchConvertTo | Buffer | Pointer | 4,698.364 ns | 14.5491 ns | 12.1492 ns | +// | BenchConvertTo | Buffer | Boolean | 6.130 ns | 0.0323 ns | 0.0302 ns | +// | BenchConvertTo | Buffer | Integer | 4,645.764 ns | 15.8146 ns | 14.7930 ns | +// | BenchConvertTo | Buffer | ByteString | 29.874 ns | 0.1518 ns | 0.1268 ns | +// | BenchConvertTo | Buffer | Buffer | 4.939 ns | 0.0190 ns | 0.0178 ns | +// | BenchConvertTo | Buffer | Array | 4,683.427 ns | 21.3813 ns | 20.0001 ns | +// | BenchConvertTo | Buffer | Struct | 4,680.762 ns | 15.7220 ns | 13.9371 ns | +// | BenchConvertTo | Buffer | Map | 4,706.510 ns | 14.2061 ns | 12.5934 ns | +// | BenchConvertTo | Buffer | InteropInterface | 4,703.050 ns | 15.8002 ns | 14.0064 ns | +// | BenchConvertTo | Array | Any | 4,652.710 ns | 23.2061 ns | 20.5716 ns | +// | BenchConvertTo | Array | Pointer | 4,625.049 ns | 12.4455 ns | 11.6415 ns | +// | BenchConvertTo | Array | Boolean | 5.568 ns | 0.0181 ns | 0.0169 ns | +// | BenchConvertTo | Array | Integer | 4,659.897 ns | 19.8036 ns | 18.5243 ns | +// | BenchConvertTo | Array | ByteString | 4,663.020 ns | 12.4988 ns | 11.6914 ns | +// | BenchConvertTo | Array | Buffer | 4,680.281 ns | 14.9748 ns | 13.2748 ns | +// | BenchConvertTo | Array | Array | 4.246 ns | 0.0124 ns | 0.0110 ns | +// | BenchConvertTo | Array | Struct | 1,193.106 ns | 98.5374 ns | 285.8748 ns | +// | BenchConvertTo | Array | Map | 4,742.631 ns | 35.5855 ns | 33.2867 ns | +// | BenchConvertTo | Array | InteropInterface | 4,670.743 ns | 9.3547 ns | 7.8116 ns | +// | BenchConvertTo | Struct | Any | 4,643.558 ns | 31.0451 ns | 29.0396 ns | +// | BenchConvertTo | Struct | Pointer | 4,867.925 ns | 22.2347 ns | 19.7105 ns | +// | BenchConvertTo | Struct | Boolean | 5.581 ns | 0.0251 ns | 0.0235 ns | +// | BenchConvertTo | Struct | Integer | 4,653.442 ns | 17.7417 ns | 16.5956 ns | +// | BenchConvertTo | Struct | ByteString | 4,646.242 ns | 13.7830 ns | 12.8926 ns | +// | BenchConvertTo | Struct | Buffer | 4,776.205 ns | 14.1918 ns | 13.2751 ns | +// | BenchConvertTo | Struct | Array | 1,622.573 ns | 144.8116 ns | 398.8532 ns | +// | BenchConvertTo | Struct | Struct | 4.195 ns | 0.0327 ns | 0.0290 ns | +// | BenchConvertTo | Struct | Map | 4,672.579 ns | 17.6257 ns | 16.4871 ns | +// | BenchConvertTo | Struct | InteropInterface | 4,653.476 ns | 8.2047 ns | 7.6747 ns | +// | BenchConvertTo | Map | Any | 4,676.540 ns | 15.2010 ns | 13.4753 ns | +// | BenchConvertTo | Map | Pointer | 4,663.489 ns | 13.7871 ns | 12.2219 ns | +// | BenchConvertTo | Map | Boolean | 5.535 ns | 0.0205 ns | 0.0192 ns | +// | BenchConvertTo | Map | Integer | 4,661.275 ns | 12.4402 ns | 11.6366 ns | +// | BenchConvertTo | Map | ByteString | 4,662.482 ns | 25.7111 ns | 24.0502 ns | +// | BenchConvertTo | Map | Buffer | 4,859.809 ns | 18.2981 ns | 16.2208 ns | +// | BenchConvertTo | Map | Array | 4,627.149 ns | 10.7487 ns | 9.5285 ns | +// | BenchConvertTo | Map | Struct | 4,646.504 ns | 22.4190 ns | 20.9707 ns | +// | BenchConvertTo | Map | Map | 4.160 ns | 0.0180 ns | 0.0169 ns | +// | BenchConvertTo | Map | InteropInterface | 4,667.024 ns | 14.1790 ns | 13.2630 ns | +// | BenchConvertTo | InteropInterface | Any | 4,700.511 ns | 17.4725 ns | 15.4889 ns | +// | BenchConvertTo | InteropInterface | Pointer | 4,705.819 ns | 25.2035 ns | 23.5754 ns | +// | BenchConvertTo | InteropInterface | Boolean | 5.557 ns | 0.0244 ns | 0.0228 ns | +// | BenchConvertTo | InteropInterface | Integer | 4,695.410 ns | 21.8674 ns | 20.4547 ns | +// | BenchConvertTo | InteropInterface | ByteString | 4,674.552 ns | 18.8705 ns | 17.6515 ns | +// | BenchConvertTo | InteropInterface | Buffer | 4,649.237 ns | 23.9084 ns | 22.3639 ns | +// | BenchConvertTo | InteropInterface | Array | 4,827.652 ns | 29.7153 ns | 27.7957 ns | +// | BenchConvertTo | InteropInterface | Struct | 4,624.202 ns | 10.3563 ns | 8.0855 ns | +// | BenchConvertTo | InteropInterface | Map | 4,695.310 ns | 23.1192 ns | 21.6257 ns | +// | BenchConvertTo | InteropInterface | InteropInterface | 4.137 ns | 0.0156 ns | 0.0138 ns | diff --git a/benchmarks/Neo.VM.Benchmarks/Benchmarks.Types.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs similarity index 97% rename from benchmarks/Neo.VM.Benchmarks/Benchmarks.Types.cs rename to benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs index 3685e45f12..7991608b8c 100644 --- a/benchmarks/Neo.VM.Benchmarks/Benchmarks.Types.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2024 The Neo Project. // -// Benchmarks.Types.cs file belongs to the neo project and is free +// Benchmarks_DeepCopy.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 @@ -14,7 +14,7 @@ namespace Neo.VM.Benchmark; -public class Benchmarks_Types +public class Benchmarks_DeepCopy { public IEnumerable<(int Depth, int ElementsPerLevel)> ParamSource() { From 6067f670fedbb0e6830a95db5a79dbfb9d5e1b93 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 8 Oct 2024 11:27:54 +0300 Subject: [PATCH 03/53] OpCodes: extend MODMUL tests with negative base/multiplier/mod (#3513) Allows to avoid bugs like https://github.com/nspcc-dev/neo-go/issues/3598. Signed-off-by: Anna Shaleva Co-authored-by: Jimmy --- .../Tests/OpCodes/Arithmetic/MODMUL.json | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json index e3b93bfb56..1c2a9d9dab 100644 --- a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODMUL.json @@ -136,6 +136,242 @@ } } ] + }, + { + "name": "Real test (-3 * 4 % 5)", + "script": [ + "PUSH3", + "NEGATE", + "PUSH4", + "PUSH5", + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "MODMUL", + "evaluationStack": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": -3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + } + ] + }, + { + "name": "Real test (3 * 4 % -5)", + "script": [ + "PUSH3", + "PUSH4", + "PUSH5", + "NEGATE", + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "MODMUL", + "evaluationStack": [ + { + "type": "Integer", + "value": -5 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 2 + } + ] + } + } + ] + }, + { + "name": "Real test (-3 * 4 % -5)", + "script": [ + "PUSH3", + "NEGATE", + "PUSH4", + "PUSH5", + "NEGATE", + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "MODMUL", + "evaluationStack": [ + { + "type": "Integer", + "value": -5 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": -3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + } + ] + }, + { + "name": "Real test (3 * -4 % -5)", + "script": [ + "PUSH3", + "PUSH4", + "NEGATE", + "PUSH5", + "NEGATE", + "MODMUL" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "MODMUL", + "evaluationStack": [ + { + "type": "Integer", + "value": -5 + }, + { + "type": "Integer", + "value": -4 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": -2 + } + ] + } + } + ] } ] } From 331fa24d754b3eeedea1800e3488c685b66ec211 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 8 Oct 2024 18:03:04 +0800 Subject: [PATCH 04/53] fix: sensitive data compare should use constant time compare to avoid timing attack (#3508) * fix: pass compare should use contant time compare to avoid timing attack * fix: pass compare should use contant time compare to avoid timing attack * Update src/Plugins/RpcServer/RpcServer.cs * Update src/Plugins/RpcServer/RpcServer.cs --------- Co-authored-by: Shargon Co-authored-by: Jimmy --- src/Plugins/RpcServer/RpcServer.cs | 24 ++++++++--- .../UT_RpcServer.cs | 42 +++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/Plugins/RpcServer/RpcServer.cs b/src/Plugins/RpcServer/RpcServer.cs index c90bbad539..0dc3d467bf 100644 --- a/src/Plugins/RpcServer/RpcServer.cs +++ b/src/Plugins/RpcServer/RpcServer.cs @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.DependencyInjection; +using Neo.Extensions; using Neo.Json; using Neo.Network.P2P; using System; @@ -26,6 +27,7 @@ using System.Linq.Expressions; using System.Net.Security; using System.Reflection; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; @@ -44,10 +46,18 @@ public partial class RpcServer : IDisposable private readonly NeoSystem system; private readonly LocalNode localNode; + // avoid GetBytes every time + private readonly byte[] _rpcUser; + private readonly byte[] _rpcPass; + public RpcServer(NeoSystem system, RpcServerSettings settings) { this.system = system; this.settings = settings; + + _rpcUser = settings.RpcUser is not null ? Encoding.UTF8.GetBytes(settings.RpcUser) : []; + _rpcPass = settings.RpcPass is not null ? Encoding.UTF8.GetBytes(settings.RpcPass) : []; + localNode = system.LocalNode.Ask(new LocalNode.GetInstance()).Result; RegisterMethods(this); Initialize_SmartContract(); @@ -65,21 +75,25 @@ internal bool CheckAuth(HttpContext context) return false; } - string authstring; + byte[] auths; try { - authstring = Encoding.UTF8.GetString(Convert.FromBase64String(reqauth.Replace("Basic ", "").Trim())); + auths = Convert.FromBase64String(reqauth.Replace("Basic ", "").Trim()); } catch { return false; } - string[] authvalues = authstring.Split(new string[] { ":" }, StringSplitOptions.RemoveEmptyEntries); - if (authvalues.Length < 2) + int colonIndex = Array.IndexOf(auths, (byte)':'); + if (colonIndex == -1) return false; - return authvalues[0] == settings.RpcUser && authvalues[1] == settings.RpcPass; + byte[] user = auths[..colonIndex]; + byte[] pass = auths[(colonIndex + 1)..]; + + // Always execute both checks, but both must evaluate to true + return CryptographicOperations.FixedTimeEquals(user, _rpcUser) & CryptographicOperations.FixedTimeEquals(pass, _rpcPass); } private static JObject CreateErrorResponse(JToken id, RpcError rpcError) diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs index b63380f803..b8425ee0a9 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.cs @@ -87,5 +87,47 @@ public void TestCheckAuth_ValidCredentials_ReturnsTrue() // Assert Assert.IsTrue(result); } + + [TestMethod] + public void TestCheckAuth() + { + var memoryStoreProvider = new TestMemoryStoreProvider(new MemoryStore()); + var neoSystem = new NeoSystem(TestProtocolSettings.SoleNode, memoryStoreProvider); + var rpcServerSettings = RpcServerSettings.Default with + { + SessionEnabled = true, + SessionExpirationTime = TimeSpan.FromSeconds(0.3), + MaxGasInvoke = 1500_0000_0000, + Network = TestProtocolSettings.SoleNode.Network, + RpcUser = "testuser", + RpcPass = "testpass", + }; + var rpcServer = new RpcServer(neoSystem, rpcServerSettings); + + var context = new DefaultHttpContext(); + context.Request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes("testuser:testpass")); + var result = rpcServer.CheckAuth(context); + Assert.IsTrue(result); + + context.Request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes("testuser:wrongpass")); + result = rpcServer.CheckAuth(context); + Assert.IsFalse(result); + + context.Request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes("wronguser:testpass")); + result = rpcServer.CheckAuth(context); + Assert.IsFalse(result); + + context.Request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes("testuser:")); + result = rpcServer.CheckAuth(context); + Assert.IsFalse(result); + + context.Request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(":testpass")); + result = rpcServer.CheckAuth(context); + Assert.IsFalse(result); + + context.Request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes("")); + result = rpcServer.CheckAuth(context); + Assert.IsFalse(result); + } } } From a817e2918e62cf9d43ae0aaa67addfbafb6f7759 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 9 Oct 2024 05:58:46 +0200 Subject: [PATCH 05/53] Related to https://github.com/neo-project/neo/pull/3508#discussion_r1788226279 (#3516) Co-authored-by: Jimmy --- .../ConstantTimeUtility.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs b/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs index fefde72b67..3bfc1226e6 100644 --- a/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs +++ b/src/Neo.Cryptography.BLS12_381/ConstantTimeUtility.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; namespace Neo.Cryptography.BLS12_381; @@ -18,16 +19,10 @@ public static class ConstantTimeUtility { public static bool ConstantTimeEq(in T a, in T b) where T : unmanaged { - ReadOnlySpan a_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in a), 1)); - ReadOnlySpan b_bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in b), 1)); - ReadOnlySpan a_u64 = MemoryMarshal.Cast(a_bytes); - ReadOnlySpan b_u64 = MemoryMarshal.Cast(b_bytes); - ulong f = 0; - for (int i = 0; i < a_u64.Length; i++) - f |= a_u64[i] ^ b_u64[i]; - for (int i = a_u64.Length * sizeof(ulong); i < a_bytes.Length; i++) - f |= (ulong)a_bytes[i] ^ b_bytes[i]; - return f == 0; + 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); } public static T ConditionalSelect(in T a, in T b, bool choice) where T : unmanaged From 82906978e4ab18178294a93a4275d51aa88d05e1 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Wed, 9 Oct 2024 14:13:53 +0800 Subject: [PATCH 06/53] [Benchmark] this pr adds more pocs to benchmark (#3512) * this pr adds more pocs to benchmark * format --------- Co-authored-by: Fernando Diaz Toledano --- .../Neo.VM.Benchmarks/Benchmarks.POC.cs | 292 +++++++++++++++++- 1 file changed, 288 insertions(+), 4 deletions(-) diff --git a/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs b/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs index 1a5dac4b97..2eede59fc3 100644 --- a/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs +++ b/benchmarks/Neo.VM.Benchmarks/Benchmarks.POC.cs @@ -46,7 +46,7 @@ public void NeoIssue2528() // L24: LDLOC 0 // L25: JMPIF_L L19 // L26: DROP - Run(nameof(NeoIssue2528), "VwEAwkpKAfsHdwARwG8AnXcAbwAl9////xHAzwJwlAAAdwAQzm8AnXcAbwAl9////0U="); + Run("VwEAwkpKAfsHdwARwG8AnXcAbwAl9////xHAzwJwlAAAdwAQzm8AnXcAbwAl9////0U="); } [Benchmark] @@ -81,7 +81,7 @@ public void NeoVMIssue418() // L25: DROP // L26: ROT // L27: DROP - Run(nameof(NeoVMIssue418), "whBNEcARTRHAVgEB/gGdYBFNEU0SwFMSwFhKJPNFUUU="); + Run("whBNEcARTRHAVgEB/gGdYBFNEU0SwFMSwFhKJPNFUUU="); } [Benchmark] @@ -98,15 +98,299 @@ public void NeoIssue2723() // L08: DUP // L09: STSFLD 0 // L10: JMPIF L03 - Run(nameof(NeoIssue2723), "VgEC0PsBAGcAAgAAEACIRV8AnUpnACTz"); + Run("VgEC0PsBAGcAAgAAEACIRV8AnUpnACTz"); } - private static void Run(string name, string poc) + // Below are PoCs from issue https://github.com/neo-project/neo/issues/2723 by @dusmart + [Benchmark] + public void PoC_NewBuffer() + { + // INITSLOT 0100 + // PUSHINT32 23000000 + // STLOC 00 + // PUSHINT32 1048576 + // NEWBUFFER + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f2ffffff + // CLEAR + // RET + Run("VwEAAsDzXgF3AAIAABAAiEVvAJ13AG8AJfL///9JQA=="); + } + + [Benchmark] + public void PoC_Cat() + { + // INITSLOT 0100 + // PUSHINT32 1048575 + // NEWBUFFER + // PUSH1 + // NEWBUFFER + // PUSHINT32 133333337 + // STLOC 00 + // OVER + // OVER + // CAT + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f5ffffff + // CLEAR + // RET + Run("VwEAAv//DwCIEYgCWYHyB3cAS0uLRW8AnXcAbwAl9f///0lA"); + } + + [Benchmark] + public void PoC_Left() + { + // INITSLOT 0100 + // PUSHINT32 1048576 + // NEWBUFFER + // PUSHINT32 133333337 + // STLOC 00 + // DUP + // PUSHINT32 1048576 + // LEFT + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f1ffffff + // CLEAR + // RET + Run("VwEAAgAAEACIAlmB8gd3AEoCAAAQAI1FbwCddwBvACXx////SUA="); + } + + [Benchmark] + public void PoC_Right() + { + // INITSLOT 0100 + // PUSHINT32 1048576 + // NEWBUFFER + // PUSHINT32 133333337 + // STLOC 00 + // DUP + // PUSHINT32 1048576 + // RIGHT + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f1ffffff + // CLEAR + // RET + Run("VwEAAgAAEACIAlmB8gd3AEoCAAAQAI5FbwCddwBvACXx////SUA="); + } + + [Benchmark] + public void PoC_ReverseN() + { + // INITSLOT 0100 + // PUSHINT16 2040 + // STLOC 00 + // PUSHDATA1 aaabbbbbbbbbcccccccdddddddeeeeeeefffffff + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L cfffffff + // PUSHINT32 23000000 + // STLOC 00 + // PUSHINT16 2040 + // REVERSEN + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f5ffffff + // CLEAR + // RET + Run("VwEAAfgHdwAMKGFhYWJiYmJiYmJiYmNjY2NjY2NkZGRkZGRkZWVlZWVlZWZmZmZmZmZvAJ13AG8AJc////8CwPNeAXcAAfgHVW8AnXcAbwAl9f///0lA"); + } + + [Benchmark] + public void PoC_Substr() + { + // INITSLOT 0100 + // PUSHINT32 1048576 + // NEWBUFFER + // PUSHINT32 133333337 + // STLOC 00 + // DUP + // PUSH0 + // PUSHINT32 1048576 + // SUBSTR + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f0ffffff + // CLEAR + // RET + Run("VwEAAgAAEACIAlmB8gd3AEoQAgAAEACMRW8AnXcAbwAl8P///0lA"); + } + + [Benchmark] + public void PoC_NewArray() + { + // INITSLOT 0100 + // PUSHINT32 1333333337 + // STLOC 00 + // PUSHINT16 2040 + // NEWARRAY + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f4ffffff + // RET + Run("VwEAAlkNeU93AAH4B8NFbwCddwBvACX0////QA=="); + } + + [Benchmark] + public void PoC_NewStruct() + { + // INITSLOT 0100 + // PUSHINT32 1333333337 + // STLOC 00 + // PUSHINT16 2040 + // NEWSTRUCT + // DROP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f4ffffff + // RET + Run("VwEAAlkNeU93AAH4B8ZFbwCddwBvACX0////QA=="); + } + + [Benchmark] + public void PoC_Roll() + { + // INITSLOT 0100 + // PUSHINT16 2040 + // STLOC 00 + // PUSHDATA1 aaabbbbbbbbbcccccccdddddddeeeeeeefffffff + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L cfffffff + // PUSHINT32 23000000 + // STLOC 00 + // PUSHINT16 2039 + // ROLL + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f5ffffff + // CLEAR + // RET + Run("VwEAAfgHdwAMKGFhYWJiYmJiYmJiYmNjY2NjY2NkZGRkZGRkZWVlZWVlZWZmZmZmZmZvAJ13AG8AJc////8CwPNeAXcAAfcHUm8AnXcAbwAl9f///0lA"); + } + + [Benchmark] + public void PoC_XDrop() + { + // INITSLOT 0100 + // PUSHINT16 2040 + // STLOC 00 + // PUSHDATA1 aaabbbbbbbbbcccccccdddddddeeeeeeefffffff + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L cfffffff + // PUSHINT32 23000000 + // STLOC 00 + // PUSHINT16 2039 + // XDROP + // DUP + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f4ffffff + // CLEAR + // RET + Run("VwEAAfgHdwAMKGFhYWJiYmJiYmJiYmNjY2NjY2NkZGRkZGRkZWVlZWVlZWZmZmZmZmZvAJ13AG8AJc////8CwPNeAXcAAfcHSEpvAJ13AG8AJfT///9JQA=="); + } + + [Benchmark] + public void PoC_MemCpy() + { + // INITSLOT 0100 + // PUSHINT32 1048576 + // NEWBUFFER + // PUSHINT32 1048576 + // NEWBUFFER + // PUSHINT32 133333337 + // STLOC 00 + // OVER + // PUSH0 + // PUSH2 + // PICK + // PUSH0 + // PUSHINT32 1048576 + // MEMCPY + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L eeffffff + // CLEAR + // RET + Run("VwEAAgAAEACIAgAAEACIAlmB8gd3AEsQEk0QAgAAEACJbwCddwBvACXu////SUA="); + } + + [Benchmark] + public void PoC_Unpack() + { + // INITSLOT 0200 + // PUSHINT16 1010 + // NEWARRAY + // STLOC 01 + // PUSHINT32 1333333337 + // STLOC 00 + // LDLOC 01 + // UNPACK + // CLEAR + // LDLOC 00 + // DEC + // STLOC 00 + // LDLOC 00 + // JMPIF_L f5ffffff + // RET + Run("VwIAAfIDw3cBAlkNeU93AG8BwUlvAJ13AG8AJfX///9A"); + } + + [Benchmark] + public void PoC_GetScriptContainer() + { + // SYSCALL System.Runtime.GetScriptContainer + // DROP + // JMP fa + Run("QS1RCDBFIvo="); + } + + private static void Run(string poc) { byte[] script = Convert.FromBase64String(poc); using ExecutionEngine engine = new(); engine.LoadScript(script); engine.Execute(); + Debug.Assert(engine.State == VMState.HALT); } } From 52393be239bce163c14ff09b15dbabd4a6bc3240 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 00:00:11 -0700 Subject: [PATCH 07/53] Bump System.Text.Json from 8.0.4 to 8.0.5 in /src/Neo.Json (#3519) Bumps [System.Text.Json](https://github.com/dotnet/runtime) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/dotnet/runtime/releases) - [Commits](https://github.com/dotnet/runtime/compare/v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: System.Text.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> Co-authored-by: Shargon --- src/Neo.Json/Neo.Json.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo.Json/Neo.Json.csproj b/src/Neo.Json/Neo.Json.csproj index 8d8fd33ac9..0a490a1934 100644 --- a/src/Neo.Json/Neo.Json.csproj +++ b/src/Neo.Json/Neo.Json.csproj @@ -10,7 +10,7 @@ - + From 5d2d8daa73f2609abe489456e22ba375ebb11cf9 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Thu, 10 Oct 2024 18:13:49 +0800 Subject: [PATCH 08/53] Circular reference error info in ToJson (#3522) Co-authored-by: Shargon --- src/Neo/VM/Helper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Neo/VM/Helper.cs b/src/Neo/VM/Helper.cs index 26d0fa132a..6964747473 100644 --- a/src/Neo/VM/Helper.cs +++ b/src/Neo/VM/Helper.cs @@ -365,7 +365,7 @@ private static JObject ToJson(StackItem item, HashSet context, ref in case Array array: { context ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!context.Add(array)) throw new InvalidOperationException(); + if (!context.Add(array)) throw new InvalidOperationException("Circular reference."); maxSize -= 2/*[]*/+ Math.Max(0, (array.Count - 1))/*,*/; JArray a = new(); foreach (StackItem stackItem in array) @@ -398,7 +398,7 @@ private static JObject ToJson(StackItem item, HashSet context, ref in case Map map: { context ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!context.Add(map)) throw new InvalidOperationException(); + if (!context.Add(map)) throw new InvalidOperationException("Circular reference."); maxSize -= 2/*[]*/+ Math.Max(0, (map.Count - 1))/*,*/; JArray a = new(); foreach (var (k, v) in map) From 20c866174c4364f644b8ca403b97b616a4e798ec Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 10 Oct 2024 22:03:31 +0800 Subject: [PATCH 09/53] [Benchmark] Benchmark OpCode and VM (#3514) * add opcode benchmark system * add opcode benchmark system * update to make the framework easier to work with. * Clean code * filescope namespace * remove uncomplet benchmark * add missing using --------- Co-authored-by: Fernando Diaz Toledano --- .../InstructionBuilder/Helper.cs | 72 ++++++ .../InstructionBuilder/Instruction.cs | 58 +++++ .../InstructionBuilder/InstructionBuilder.cs | 242 ++++++++++++++++++ .../InstructionBuilder/JumpTarget.cs | 17 ++ .../Neo.VM.Benchmarks.csproj | 4 + .../OpCode/Arrays/OpCode.ReverseN.cs | 136 ++++++++++ .../OpCode/Benchmark.Opcode.cs | 234 +++++++++++++++++ .../OpCode/BenchmarkEngine.cs | 190 ++++++++++++++ .../Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs | 19 ++ .../Neo.VM.Benchmarks/OpCode/OpCodeBase.cs | 45 ++++ benchmarks/Neo.VM.Benchmarks/Program.cs | 57 ++++- .../VMTypes/Benchmarks_Convert.cs | 1 - src/Neo.VM/OpCode.cs | 2 +- tests/Neo.VM.Tests/Types/TestEngine.cs | 2 +- 14 files changed, 1075 insertions(+), 4 deletions(-) create mode 100644 benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs create mode 100644 benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs new file mode 100644 index 0000000000..f5ea579e6f --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Helper.cs @@ -0,0 +1,72 @@ +// 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 System.Buffers.Binary; + +namespace Neo.VM.Benchmark; + +public static class Helper +{ + public static void RebuildOffsets(this IReadOnlyList instructions) + { + 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) + { + 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) + { + 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 + { + 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 new file mode 100644 index 0000000000..5a30aeec10 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/Instruction.cs @@ -0,0 +1,58 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Instruction.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.CodeAnalysis; +using System.Diagnostics; +using System.Reflection; + +namespace Neo.VM.Benchmark; + +[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 int Size + { + 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)) + { + 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(); + } +} diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs new file mode 100644 index 0000000000..21d1b77de2 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/InstructionBuilder.cs @@ -0,0 +1,242 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// InstructionBuilder.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.Buffers.Binary; +using System.Numerics; + +namespace Neo.VM.Benchmark; + +internal class InstructionBuilder +{ + internal readonly List _instructions = new(); + + public InstructionBuilder() { } + + internal Instruction AddInstruction(Instruction instruction) + { + _instructions.Add(instruction); + return instruction; + } + + internal Instruction AddInstruction(VM.OpCode opcode) + { + return AddInstruction(new Instruction + { + _opCode = opcode + }); + } + + internal Instruction Jump(VM.OpCode opcode, JumpTarget target) + { + return AddInstruction(new Instruction + { + _opCode = opcode, + _target = target + }); + } + + internal void Push(bool value) + { + AddInstruction(value ? VM.OpCode.PUSHT : VM.OpCode.PUSHF); + } + + 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 + { + 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(string s) + { + return Push(Utility.StrictUTF8.GetBytes(s)); + } + + internal Instruction Push(byte[] data) + { + 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) + { + 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) + { + 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 Instruction IsType(VM.Types.StackItemType type) + { + return AddInstruction(new Instruction + { + _opCode = VM.OpCode.ISTYPE, + _operand = [(byte)type] + }); + } + + internal Instruction ChangeType(VM.Types.StackItemType 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(); + } +} diff --git a/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs new file mode 100644 index 0000000000..246b0e5884 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/InstructionBuilder/JumpTarget.cs @@ -0,0 +1,17 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// JumpTarget.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. + +namespace Neo.VM.Benchmark; + +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 ae717e8254..d5a7909a4b 100644 --- a/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj +++ b/benchmarks/Neo.VM.Benchmarks/Neo.VM.Benchmarks.csproj @@ -9,8 +9,12 @@ + + + + diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs new file mode 100644 index 0000000000..515125ddc0 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/Arrays/OpCode.ReverseN.cs @@ -0,0 +1,136 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OpCode.ReverseN.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. + +namespace Neo.VM.Benchmark.OpCode; + +public class OpCode_ReverseN : OpCodeBase +{ + protected override byte[] CreateScript(BenchmarkMode benchmarkMode) + { + 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) + { + // 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(); + } +} + +// for 0 + +// BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.4249/23H2/2023Update/SunValley3) +// Intel Core i9-14900HX, 1 CPU, 32 logical and 24 physical cores +// .NET SDK 8.0.205 +// [Host] : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2 +// DefaultJob : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2 +// +// +// | Method | ItemCount | Mean | Error | StdDev | Median | Ratio | RatioSD | +// |--------------------- |---------- |-----------------:|--------------:|---------------:|-----------------:|----------:|---------:| +// | Bench_ReverseN | 1 | 63.43 us | 0.466 us | 0.518 us | 63.45 us | 0.99 | 0.01 | +// | Bench_OneGasReverseN | 1 | 403,904.11 us | 6,492.511 us | 6,073.099 us | 402,932.40 us | 6,315.67 | 89.44 | +// | Bench_BaseLine | 1 | 63.96 us | 0.763 us | 0.714 us | 63.92 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 2 | 62.55 us | 0.988 us | 0.924 us | 62.46 us | 0.95 | 0.02 | +// | Bench_OneGasReverseN | 2 | 424,297.10 us | 8,453.137 us | 7,493.486 us | 423,912.90 us | 6,446.21 | 118.35 | +// | Bench_BaseLine | 2 | 65.83 us | 0.845 us | 0.749 us | 65.95 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 4 | 63.93 us | 0.418 us | 0.371 us | 63.89 us | 0.95 | 0.03 | +// | Bench_OneGasReverseN | 4 | 443,708.92 us | 6,689.013 us | 6,256.907 us | 444,636.60 us | 6,560.69 | 229.86 | +// | Bench_BaseLine | 4 | 67.64 us | 1.281 us | 1.524 us | 67.79 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 8 | 66.69 us | 0.757 us | 0.671 us | 66.69 us | 1.00 | 0.02 | +// | Bench_OneGasReverseN | 8 | 463,571.38 us | 6,614.687 us | 6,187.382 us | 465,568.00 us | 6,963.59 | 85.80 | +// | Bench_BaseLine | 8 | 66.64 us | 0.870 us | 0.771 us | 66.68 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 16 | 65.85 us | 0.994 us | 0.929 us | 65.61 us | 0.94 | 0.02 | +// | Bench_OneGasReverseN | 16 | 740,905.55 us | 71,090.901 us | 209,613.127 us | 653,644.75 us | 9,341.86 | 3,092.85 | +// | Bench_BaseLine | 16 | 70.08 us | 1.376 us | 1.638 us | 70.15 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 32 | 66.47 us | 0.928 us | 2.187 us | 65.77 us | 1.02 | 0.04 | +// | Bench_OneGasReverseN | 32 | 631,596.65 us | 11,060.847 us | 10,346.323 us | 629,654.10 us | 9,360.06 | 221.36 | +// | Bench_BaseLine | 32 | 67.49 us | 0.900 us | 0.842 us | 67.56 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 64 | 72.21 us | 0.921 us | 0.862 us | 72.05 us | 1.02 | 0.02 | +// | Bench_OneGasReverseN | 64 | 787,570.95 us | 6,915.746 us | 6,468.994 us | 786,778.70 us | 11,090.76 | 177.74 | +// | Bench_BaseLine | 64 | 71.02 us | 0.946 us | 0.884 us | 71.06 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 128 | 80.17 us | 0.723 us | 0.676 us | 80.19 us | 0.98 | 0.01 | +// | Bench_OneGasReverseN | 128 | 1,134,510.61 us | 14,991.714 us | 14,023.259 us | 1,133,177.90 us | 13,828.61 | 184.58 | +// | Bench_BaseLine | 128 | 81.90 us | 0.623 us | 0.553 us | 81.77 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 256 | 98.24 us | 1.140 us | 1.067 us | 98.05 us | 0.99 | 0.01 | +// | Bench_OneGasReverseN | 256 | 1,785,906.33 us | 13,785.746 us | 12,895.195 us | 1,788,819.30 us | 18,067.20 | 198.87 | +// | Bench_BaseLine | 256 | 98.85 us | 0.961 us | 0.899 us | 98.95 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 512 | 136.19 us | 1.614 us | 1.510 us | 136.34 us | 1.02 | 0.02 | +// | Bench_OneGasReverseN | 512 | 3,100,087.41 us | 16,564.249 us | 15,494.209 us | 3,097,066.60 us | 23,209.57 | 381.50 | +// | Bench_BaseLine | 512 | 133.60 us | 2.144 us | 2.006 us | 132.73 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 1024 | 207.06 us | 2.213 us | 2.070 us | 206.76 us | 1.01 | 0.01 | +// | Bench_OneGasReverseN | 1024 | 5,762,294.72 us | 20,289.404 us | 16,942.572 us | 5,764,133.80 us | 28,109.14 | 349.87 | +// | Bench_BaseLine | 1024 | 205.07 us | 2.360 us | 2.208 us | 205.07 us | 1.00 | 0.00 | +// | | | | | | | | | +// | Bench_ReverseN | 2040 | 345.09 us | 4.271 us | 3.995 us | 345.40 us | 0.97 | 0.01 | +// | Bench_OneGasReverseN | 2040 | 11,005,147.03 us | 37,306.455 us | 33,071.200 us | 11,003,479.70 us | 31,019.36 | 356.02 | +// | Bench_BaseLine | 2040 | 354.62 us | 4.623 us | 4.325 us | 353.32 us | 1.00 | 0.00 | + + + +// for StackItems that has a size of maximum stack size. +// | Method | ItemCount | Mean | Error | StdDev | +// |--------------------- |---------- |----------------:|--------------:|--------------:| +// | Bench_ReverseN | 1 | 104.0 us | 0.77 us | 0.72 us | +// | Bench_OneGasReverseN | 1 | 389,585.4 us | 4,740.18 us | 4,433.96 us | +// | Bench_ReverseN | 2 | 148.3 us | 2.25 us | 2.10 us | +// | Bench_OneGasReverseN | 2 | 417,831.5 us | 6,651.20 us | 6,221.53 us | +// | Bench_ReverseN | 4 | 231.8 us | 3.92 us | 3.67 us | +// | Bench_OneGasReverseN | 4 | 428,442.6 us | 8,034.41 us | 7,515.39 us | +// | Bench_ReverseN | 8 | 387.8 us | 5.23 us | 4.89 us | +// | Bench_OneGasReverseN | 8 | 448,046.9 us | 6,270.18 us | 5,235.89 us | +// | Bench_ReverseN | 16 | 240.0 us | 7.30 us | 21.53 us | +// | Bench_OneGasReverseN | 16 | 522,904.3 us | 7,157.93 us | 6,695.54 us | +// | Bench_ReverseN | 32 | 302.4 us | 9.53 us | 27.79 us | +// | Bench_OneGasReverseN | 32 | 626,536.6 us | 6,629.69 us | 6,201.42 us | +// | Bench_ReverseN | 64 | 1,728.3 us | 34.27 us | 58.19 us | +// | Bench_OneGasReverseN | 64 | 827,284.5 us | 15,943.00 us | 14,913.09 us | +// | Bench_ReverseN | 128 | 3,704.5 us | 73.98 us | 175.82 us | +// | Bench_OneGasReverseN | 128 | 1,125,104.6 us | 10,629.65 us | 9,942.98 us | +// | Bench_ReverseN | 256 | 6,381.1 us | 127.42 us | 290.21 us | +// | Bench_OneGasReverseN | 256 | 1,804,355.7 us | 9,690.50 us | 8,590.37 us | +// | Bench_ReverseN | 512 | 9,485.9 us | 184.52 us | 492.52 us | +// | Bench_OneGasReverseN | 512 | 3,159,411.1 us | 28,901.54 us | 27,034.52 us | +// | Bench_ReverseN | 1024 | 14,125.6 us | 282.51 us | 577.08 us | +// | Bench_OneGasReverseN | 1024 | 5,799,154.5 us | 33,817.93 us | 31,633.31 us | +// | Bench_ReverseN | 2040 | 22,868.0 us | 449.84 us | 929.00 us | +// | Bench_OneGasReverseN | 2040 | 11,100,853.9 us | 159,980.97 us | 141,818.97 us | diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs new file mode 100644 index 0000000000..f55abae7ee --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/Benchmark.Opcode.cs @@ -0,0 +1,234 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Benchmark.Opcode.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. + +namespace Neo.VM.Benchmark.OpCode; + +public class Benchmark_Opcode +{ + internal static readonly long OneGasDatoshi = 1_0000_0000; + + 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 void RunScript(byte[] script) + { + LoadScript(script).ExecuteBenchmark(); + } + + 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 new file mode 100644 index 0000000000..50f80c57e9 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkEngine.cs @@ -0,0 +1,190 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BenchmarkEngine.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.Test.Types; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Neo.VM.Benchmark.OpCode; + +/// +/// A simple benchmark engine for . +/// +public class BenchmarkEngine : TestEngine +{ + 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. + /// + /// The script to add the breakpoint. + /// The position of the breakpoint in the script. + public void AddBreakPoint(Script script, uint position) + { + if (!_breakPoints.TryGetValue(script, out var hashset)) + { + hashset = []; + _breakPoints.Add(script, hashset); + } + 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) + { + ExecuteNext(); + try + { + var instruction = CurrentContext!.CurrentInstruction!.OpCode; + if (instruction == opCode) break; + } + catch + { + break; + } + } + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteBenchmark() + { + while (State != VMState.HALT && State != VMState.FAULT) + { +#if DEBUG + var stopwatch = Stopwatch.StartNew(); +#endif + ExecuteNext(); +#if DEBUG + stopwatch.Stop(); + UpdateOpcodeStats(CurrentContext!.CurrentInstruction!.OpCode, stopwatch.Elapsed); +#endif + } +#if DEBUG + PrintOpcodeStats(); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteOneGASBenchmark() + { + while (State != VMState.HALT && State != VMState.FAULT) + { + 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(); +#endif + ExecuteNext(); +#if DEBUG + stopwatch.Stop(); + UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); +#endif + } +#if DEBUG + PrintOpcodeStats(); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteTwentyGASBenchmark() + { + while (State != VMState.HALT && State != VMState.FAULT) + { + 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(); +#endif + ExecuteNext(); +#if DEBUG + stopwatch.Stop(); + UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); +#endif + } +#if DEBUG + PrintOpcodeStats(); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExecuteOpCodesBenchmark() + { + while (State != VMState.HALT && State != VMState.FAULT) + { + 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(); +#endif + ExecuteNext(); +#if DEBUG + stopwatch.Stop(); + UpdateOpcodeStats(instruction.OpCode, stopwatch.Elapsed); +#endif + } +#if DEBUG + 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)) + { + _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) + { + 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 new file mode 100644 index 0000000000..792ff04f16 --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs @@ -0,0 +1,19 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// BenchmarkMode.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. + +namespace Neo.VM.Benchmark.OpCode; + +public enum BenchmarkMode +{ + SimpleOpCode, + OneGAS, + BaseLine +} diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs new file mode 100644 index 0000000000..78aa0dd24c --- /dev/null +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs @@ -0,0 +1,45 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OpCodeBase.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; + +namespace Neo.VM.Benchmark.OpCode; + +public abstract class OpCodeBase +{ + [Params(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2040)] + public int ItemCount { get; set; } = 10; + protected byte[] baseLineScript; + protected byte[] script; + protected byte[] multiScript; + + [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); +} diff --git a/benchmarks/Neo.VM.Benchmarks/Program.cs b/benchmarks/Neo.VM.Benchmarks/Program.cs index 29b4549674..09523ecd7c 100644 --- a/benchmarks/Neo.VM.Benchmarks/Program.cs +++ b/benchmarks/Neo.VM.Benchmarks/Program.cs @@ -9,7 +9,62 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using Neo.VM.Benchmark; +using System.Reflection; -BenchmarkRunner.Run(); +// Flag to determine if running benchmark or running methods +// If `NEO_VM_BENCHMARK` environment variable is set, run benchmark no matter. +var runBenchmark = true; + +// Define the benchmark or execute class +var benchmarkType = typeof(Benchmarks_PoCs); + +/* + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | DO NOT MODIFY THE CODE BELOW | + | | + | All configuration should be done above this line | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +// Explanation: +// Benchmark methods must contain no parameters to be valid. +// This is because we need to be able to invoke these methods repeatedly +// without any external input. All necessary data should be set up in the Setup method +// or as properties of the benchmark class. + +// Example: + +// [Benchmark] +// public void BenchmarkMethod() +// { +// // Benchmark code here +// } +if (Environment.GetEnvironmentVariable("NEO_VM_BENCHMARK") != null || runBenchmark) +{ + BenchmarkRunner.Run(benchmarkType); +} +else +{ + var instance = Activator.CreateInstance(benchmarkType); + var setupMethod = benchmarkType.GetMethods(BindingFlags.Public | BindingFlags.Instance) + .FirstOrDefault(m => m.GetCustomAttribute() != null); + if (setupMethod != null) + { + setupMethod.Invoke(instance, null); + } + + var methods = benchmarkType.GetMethods(BindingFlags.Public | BindingFlags.Instance); + + foreach (var method in methods) + { + if (method.DeclaringType == benchmarkType && !method.GetCustomAttributes().Any()) + { + method.Invoke(instance, null); + } + } +} diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs index 0860749955..91c660ff48 100644 --- a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs @@ -11,7 +11,6 @@ using BenchmarkDotNet.Attributes; using Neo.VM.Types; -using System.Collections.Generic; using Array = Neo.VM.Types.Array; using Buffer = Neo.VM.Types.Buffer; diff --git a/src/Neo.VM/OpCode.cs b/src/Neo.VM/OpCode.cs index 6af023cb6a..8a2878473c 100644 --- a/src/Neo.VM/OpCode.cs +++ b/src/Neo.VM/OpCode.cs @@ -1330,7 +1330,7 @@ public enum OpCode : byte /// /// /// Push: 1 item(s) - /// Pop: 0 item(s) + /// Pop: 1 item(s) /// /// NEWBUFFER = 0x88, diff --git a/tests/Neo.VM.Tests/Types/TestEngine.cs b/tests/Neo.VM.Tests/Types/TestEngine.cs index cf99314892..2602163315 100644 --- a/tests/Neo.VM.Tests/Types/TestEngine.cs +++ b/tests/Neo.VM.Tests/Types/TestEngine.cs @@ -15,7 +15,7 @@ namespace Neo.Test.Types { - class TestEngine : ExecutionEngine + public class TestEngine : ExecutionEngine { public Exception FaultException { get; private set; } From 018e17b2498b20fed953461ccf36e2a0ab34f8e9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 11 Oct 2024 09:29:30 +0200 Subject: [PATCH 10/53] Move `ReferenceCounter` to an interface (#3524) * Reference counter V2 * Remove warning * Interface * Change to interface * Update * Remove V2 --- benchmarks/Neo.VM.Benchmarks/TestArray.cs | 2 +- benchmarks/Neo.VM.Benchmarks/TestStruct.cs | 2 +- .../VMTypes/Benchmarks_DeepCopy.cs | 4 +- src/Neo.VM/EvaluationStack.cs | 8 +- src/Neo.VM/ExecutionContext.SharedStates.cs | 2 +- src/Neo.VM/ExecutionContext.cs | 2 +- src/Neo.VM/ExecutionEngine.cs | 2 +- src/Neo.VM/IReferenceCounter.cs | 91 +++++++++++++++++++ src/Neo.VM/ReferenceCounter.cs | 72 +++------------ src/Neo.VM/Slot.cs | 6 +- src/Neo.VM/Types/Array.cs | 2 +- src/Neo.VM/Types/CompoundType.cs | 4 +- src/Neo.VM/Types/Map.cs | 2 +- src/Neo.VM/Types/Struct.cs | 2 +- .../P2P/Payloads/Conditions/AndCondition.cs | 2 +- .../Payloads/Conditions/BooleanCondition.cs | 2 +- .../Conditions/CalledByContractCondition.cs | 2 +- .../Conditions/CalledByGroupCondition.cs | 2 +- .../P2P/Payloads/Conditions/GroupCondition.cs | 2 +- .../P2P/Payloads/Conditions/NotCondition.cs | 2 +- .../P2P/Payloads/Conditions/OrCondition.cs | 2 +- .../Conditions/ScriptHashCondition.cs | 2 +- .../Payloads/Conditions/WitnessCondition.cs | 2 +- src/Neo/Network/P2P/Payloads/Signer.cs | 2 +- src/Neo/Network/P2P/Payloads/Transaction.cs | 2 +- src/Neo/Network/P2P/Payloads/WitnessRule.cs | 2 +- src/Neo/SmartContract/BinarySerializer.cs | 12 +-- src/Neo/SmartContract/ContractState.cs | 2 +- src/Neo/SmartContract/IInteroperable.cs | 4 +- src/Neo/SmartContract/Iterators/IIterator.cs | 2 +- .../Iterators/StorageIterator.cs | 2 +- src/Neo/SmartContract/JsonSerializer.cs | 6 +- src/Neo/SmartContract/Manifest/ContractAbi.cs | 2 +- .../Manifest/ContractEventDescriptor.cs | 2 +- .../SmartContract/Manifest/ContractGroup.cs | 2 +- .../Manifest/ContractManifest.cs | 2 +- .../Manifest/ContractMethodDescriptor.cs | 2 +- .../Manifest/ContractParameterDefinition.cs | 2 +- .../Manifest/ContractPermission.cs | 2 +- src/Neo/SmartContract/Native/AccountState.cs | 2 +- .../SmartContract/Native/HashIndexState.cs | 2 +- .../SmartContract/Native/InteroperableList.cs | 4 +- src/Neo/SmartContract/Native/NeoToken.cs | 6 +- .../SmartContract/Native/OracleContract.cs | 2 +- src/Neo/SmartContract/Native/OracleRequest.cs | 2 +- .../SmartContract/Native/RoleManagement.cs | 2 +- .../SmartContract/Native/TransactionState.cs | 2 +- src/Neo/SmartContract/Native/TrimmedBlock.cs | 2 +- src/Neo/SmartContract/NotifyEventArgs.cs | 4 +- 49 files changed, 170 insertions(+), 125 deletions(-) create mode 100644 src/Neo.VM/IReferenceCounter.cs diff --git a/benchmarks/Neo.VM.Benchmarks/TestArray.cs b/benchmarks/Neo.VM.Benchmarks/TestArray.cs index 62fedfed11..6a7dbcabc1 100644 --- a/benchmarks/Neo.VM.Benchmarks/TestArray.cs +++ b/benchmarks/Neo.VM.Benchmarks/TestArray.cs @@ -57,7 +57,7 @@ public TestArray(IEnumerable? items = null) /// /// The to be used by this array. /// The items to be included in the array. - public TestArray(ReferenceCounter? referenceCounter, IEnumerable? items = null) + public TestArray(IReferenceCounter? referenceCounter, IEnumerable? items = null) : base(referenceCounter) { _array = items switch diff --git a/benchmarks/Neo.VM.Benchmarks/TestStruct.cs b/benchmarks/Neo.VM.Benchmarks/TestStruct.cs index 5a9541f1e0..8f77d680c5 100644 --- a/benchmarks/Neo.VM.Benchmarks/TestStruct.cs +++ b/benchmarks/Neo.VM.Benchmarks/TestStruct.cs @@ -31,7 +31,7 @@ public TestStruct(IEnumerable? fields = null) /// /// The to be used by this structure. /// The fields to be included in the structure. - public TestStruct(ReferenceCounter? referenceCounter, IEnumerable? fields = null) + public TestStruct(IReferenceCounter? referenceCounter, IEnumerable? fields = null) : base(referenceCounter, fields) { } diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs index 7991608b8c..1d9c267f5f 100644 --- a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs @@ -70,7 +70,7 @@ public void BenchNestedTestArrayDeepCopyWithReferenceCounter() _ = root.DeepCopy(); } - private static void CreateNestedArray(Array? rootArray, int depth, int elementsPerLevel = 1, ReferenceCounter? referenceCounter = null) + private static void CreateNestedArray(Array? rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter? referenceCounter = null) { if (depth < 0) { @@ -95,7 +95,7 @@ private static void CreateNestedArray(Array? rootArray, int depth, int elementsP } } - private static void CreateNestedTestArray(TestArray rootArray, int depth, int elementsPerLevel = 1, ReferenceCounter referenceCounter = null) + private static void CreateNestedTestArray(TestArray rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter referenceCounter = null) { if (depth < 0) { diff --git a/src/Neo.VM/EvaluationStack.cs b/src/Neo.VM/EvaluationStack.cs index 571cb6b2db..463df8d12a 100644 --- a/src/Neo.VM/EvaluationStack.cs +++ b/src/Neo.VM/EvaluationStack.cs @@ -23,12 +23,12 @@ namespace Neo.VM /// public sealed class EvaluationStack : IReadOnlyList { - private readonly List innerList = new(); - private readonly ReferenceCounter referenceCounter; + private readonly List innerList = []; + private readonly IReferenceCounter referenceCounter; - internal ReferenceCounter ReferenceCounter => referenceCounter; + internal IReferenceCounter ReferenceCounter => referenceCounter; - internal EvaluationStack(ReferenceCounter referenceCounter) + internal EvaluationStack(IReferenceCounter referenceCounter) { this.referenceCounter = referenceCounter; } diff --git a/src/Neo.VM/ExecutionContext.SharedStates.cs b/src/Neo.VM/ExecutionContext.SharedStates.cs index afa01d7995..2e9a702b9d 100644 --- a/src/Neo.VM/ExecutionContext.SharedStates.cs +++ b/src/Neo.VM/ExecutionContext.SharedStates.cs @@ -23,7 +23,7 @@ private class SharedStates public Slot? StaticFields; public readonly Dictionary States; - public SharedStates(Script script, ReferenceCounter referenceCounter) + public SharedStates(Script script, IReferenceCounter referenceCounter) { Script = script; EvaluationStack = new EvaluationStack(referenceCounter); diff --git a/src/Neo.VM/ExecutionContext.cs b/src/Neo.VM/ExecutionContext.cs index d1f9a54b1d..67e0889208 100644 --- a/src/Neo.VM/ExecutionContext.cs +++ b/src/Neo.VM/ExecutionContext.cs @@ -107,7 +107,7 @@ public Instruction? NextInstruction } } - internal ExecutionContext(Script script, int rvcount, ReferenceCounter referenceCounter) + internal ExecutionContext(Script script, int rvcount, IReferenceCounter referenceCounter) : this(new SharedStates(script, referenceCounter), rvcount, 0) { } diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index bec60c4348..b2c04bdc93 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -34,7 +34,7 @@ public class ExecutionEngine : IDisposable /// /// Used for reference counting of objects in the VM. /// - public ReferenceCounter ReferenceCounter { get; } + public IReferenceCounter ReferenceCounter { get; } /// /// The invocation stack of the VM. diff --git a/src/Neo.VM/IReferenceCounter.cs b/src/Neo.VM/IReferenceCounter.cs new file mode 100644 index 0000000000..f451c0e316 --- /dev/null +++ b/src/Neo.VM/IReferenceCounter.cs @@ -0,0 +1,91 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// IReferenceCounter.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.Types; + +namespace Neo.VM +{ + /// + /// Used for reference counting of objects in the VM. + /// + public interface IReferenceCounter + { + /// + /// Gets the count of references. + /// + int Count { get; } + + /// + /// Adds an item to the zero-referred list. + /// + /// This method is used when an item has no remaining references. + /// It adds the item to the zero-referred list to be checked for cleanup later. + /// + /// Use this method when you detect that an item has zero references and may need to be cleaned up. + /// + /// The item to add. + void AddZeroReferred(StackItem item); + + /// + /// Adds a reference to a specified item with a parent compound type. + /// + /// This method is used when an item gains a new reference through a parent compound type. + /// It increments the reference count and updates the tracking structures if necessary. + /// + /// Use this method when you need to add a reference from a compound type to a stack item. + /// + /// The item to add a reference to. + /// The parent compound type. + void AddReference(StackItem item, CompoundType parent); + + /// + /// Adds a stack reference to a specified item with a count. + /// + /// This method is used when an item gains a new stack reference, usually due to being pushed onto the evaluation stack. + /// It increments the reference count and updates the tracking structures if necessary. + /// + /// Use this method when you need to add one or more stack references to a stack item. + /// + /// The item to add a stack reference to. + /// The number of references to add. + void AddStackReference(StackItem item, int count = 1); + + /// + /// Removes a reference from a specified item with a parent compound type. + /// + /// This method is used when an item loses a reference from a parent compound type. + /// It decrements the reference count and updates the tracking structures if necessary. + /// + /// Use this method when you need to remove a reference from a compound type to a stack item. + /// + /// The item to remove a reference from. + /// The parent compound type. + void RemoveReference(StackItem item, CompoundType parent); + + /// + /// Removes a stack reference from a specified item. + /// + /// This method is used when an item loses a stack reference, usually due to being popped off the evaluation stack. + /// It decrements the reference count and updates the tracking structures if necessary. + /// + /// Use this method when you need to remove one or more stack references from a stack item. + /// + /// The item to remove a stack reference from. + void RemoveStackReference(StackItem item); + + /// + /// Checks and processes items that have zero references. + /// This method is used to check items in the zero-referred list and clean up those that are no longer needed. + /// + /// The current reference count. + int CheckZeroReferred(); + } +} diff --git a/src/Neo.VM/ReferenceCounter.cs b/src/Neo.VM/ReferenceCounter.cs index f9ea08a2e2..2ba7d74063 100644 --- a/src/Neo.VM/ReferenceCounter.cs +++ b/src/Neo.VM/ReferenceCounter.cs @@ -20,7 +20,7 @@ namespace Neo.VM /// /// Used for reference counting of objects in the VM. /// - public sealed class ReferenceCounter + public sealed class ReferenceCounter : IReferenceCounter { // If set to true, all items will be tracked regardless of their type. private const bool TrackAllItems = false; @@ -38,9 +38,7 @@ public sealed class ReferenceCounter // Keeps the total count of references. private int _referencesCount = 0; - /// - /// Gets the count of references. - /// + /// public int Count => _referencesCount; /// @@ -61,17 +59,8 @@ private static bool NeedTrack(StackItem item) return false; } - /// - /// Adds a reference to a specified item with a parent compound type. - /// - /// This method is used when an item gains a new reference through a parent compound type. - /// It increments the reference count and updates the tracking structures if necessary. - /// - /// Use this method when you need to add a reference from a compound type to a stack item. - /// - /// The item to add a reference to. - /// The parent compound type. - internal void AddReference(StackItem item, CompoundType parent) + /// + public void AddReference(StackItem item, CompoundType parent) { // Increment the reference count. _referencesCount++; @@ -98,17 +87,8 @@ internal void AddReference(StackItem item, CompoundType parent) pEntry.References++; } - /// - /// Adds a stack reference to a specified item with a count. - /// - /// This method is used when an item gains a new stack reference, usually due to being pushed onto the evaluation stack. - /// It increments the reference count and updates the tracking structures if necessary. - /// - /// Use this method when you need to add one or more stack references to a stack item. - /// - /// The item to add a stack reference to. - /// The number of references to add. - internal void AddStackReference(StackItem item, int count = 1) + /// + public void AddStackReference(StackItem item, int count = 1) { // Increment the reference count by the specified count. _referencesCount += count; @@ -127,16 +107,8 @@ internal void AddStackReference(StackItem item, int count = 1) _zeroReferred.Remove(item); } - /// - /// Adds an item to the zero-referred list. - /// - /// This method is used when an item has no remaining references. - /// It adds the item to the zero-referred list to be checked for cleanup later. - /// - /// Use this method when you detect that an item has zero references and may need to be cleaned up. - /// - /// The item to add. - internal void AddZeroReferred(StackItem item) + /// + public void AddZeroReferred(StackItem item) { // Add the item to the _zeroReferred set. _zeroReferred.Add(item); @@ -158,7 +130,7 @@ internal void AddZeroReferred(StackItem item) /// Use this method periodically to clean up items with zero references and free up memory. /// /// The current reference count. - internal int CheckZeroReferred() + public int CheckZeroReferred() { // If there are items with zero references, process them. if (_zeroReferred.Count > 0) @@ -241,18 +213,8 @@ internal int CheckZeroReferred() return _referencesCount; } - - /// - /// Removes a reference from a specified item with a parent compound type. - /// - /// This method is used when an item loses a reference from a parent compound type. - /// It decrements the reference count and updates the tracking structures if necessary. - /// - /// Use this method when you need to remove a reference from a compound type to a stack item. - /// - /// The item to remove a reference from. - /// The parent compound type. - internal void RemoveReference(StackItem item, CompoundType parent) + /// + public void RemoveReference(StackItem item, CompoundType parent) { // Decrement the reference count. _referencesCount--; @@ -271,16 +233,8 @@ internal void RemoveReference(StackItem item, CompoundType parent) _zeroReferred.Add(item); } - /// - /// Removes a stack reference from a specified item. - /// - /// This method is used when an item loses a stack reference, usually due to being popped off the evaluation stack. - /// It decrements the reference count and updates the tracking structures if necessary. - /// - /// Use this method when you need to remove one or more stack references from a stack item. - /// - /// The item to remove a stack reference from. - internal void RemoveStackReference(StackItem item) + /// + public void RemoveStackReference(StackItem item) { // Decrement the reference count. _referencesCount--; diff --git a/src/Neo.VM/Slot.cs b/src/Neo.VM/Slot.cs index 7812694ce6..30a1cb9a44 100644 --- a/src/Neo.VM/Slot.cs +++ b/src/Neo.VM/Slot.cs @@ -20,7 +20,7 @@ namespace Neo.VM /// public class Slot : IReadOnlyList { - private readonly ReferenceCounter referenceCounter; + private readonly IReferenceCounter referenceCounter; private readonly StackItem[] items; /// @@ -53,7 +53,7 @@ internal set /// /// The items to be contained. /// The reference counter to be used. - public Slot(StackItem[] items, ReferenceCounter referenceCounter) + public Slot(StackItem[] items, IReferenceCounter referenceCounter) { this.referenceCounter = referenceCounter; this.items = items; @@ -66,7 +66,7 @@ public Slot(StackItem[] items, ReferenceCounter referenceCounter) /// /// Indicates the number of items contained in the slot. /// The reference counter to be used. - public Slot(int count, ReferenceCounter referenceCounter) + public Slot(int count, IReferenceCounter referenceCounter) { this.referenceCounter = referenceCounter; items = new StackItem[count]; diff --git a/src/Neo.VM/Types/Array.cs b/src/Neo.VM/Types/Array.cs index 76c1486778..831bc830e6 100644 --- a/src/Neo.VM/Types/Array.cs +++ b/src/Neo.VM/Types/Array.cs @@ -66,7 +66,7 @@ public Array(IEnumerable? items = null) /// /// The to be used by this array. /// The items to be included in the array. - public Array(ReferenceCounter? referenceCounter, IEnumerable? items = null) + public Array(IReferenceCounter? referenceCounter, IEnumerable? items = null) : base(referenceCounter) { _array = items switch diff --git a/src/Neo.VM/Types/CompoundType.cs b/src/Neo.VM/Types/CompoundType.cs index daa9b05be6..6b0da819ec 100644 --- a/src/Neo.VM/Types/CompoundType.cs +++ b/src/Neo.VM/Types/CompoundType.cs @@ -24,13 +24,13 @@ public abstract class CompoundType : StackItem /// /// The reference counter used to count the items in the VM object. /// - protected internal readonly ReferenceCounter? ReferenceCounter; + protected internal readonly IReferenceCounter? ReferenceCounter; /// /// Create a new with the specified reference counter. /// /// The reference counter to be used. - protected CompoundType(ReferenceCounter? referenceCounter) + protected CompoundType(IReferenceCounter? referenceCounter) { ReferenceCounter = referenceCounter; referenceCounter?.AddZeroReferred(this); diff --git a/src/Neo.VM/Types/Map.cs b/src/Neo.VM/Types/Map.cs index 48155b5207..2986399d59 100644 --- a/src/Neo.VM/Types/Map.cs +++ b/src/Neo.VM/Types/Map.cs @@ -86,7 +86,7 @@ public StackItem this[PrimitiveType key] /// Create a new map with the specified reference counter. /// /// The reference counter to be used. - public Map(ReferenceCounter? referenceCounter = null) + public Map(IReferenceCounter? referenceCounter = null) : base(referenceCounter) { } diff --git a/src/Neo.VM/Types/Struct.cs b/src/Neo.VM/Types/Struct.cs index 8170414363..f2ae144d3e 100644 --- a/src/Neo.VM/Types/Struct.cs +++ b/src/Neo.VM/Types/Struct.cs @@ -35,7 +35,7 @@ public Struct(IEnumerable? fields = null) /// /// The to be used by this structure. /// The fields to be included in the structure. - public Struct(ReferenceCounter? referenceCounter, IEnumerable? fields = null) + public Struct(IReferenceCounter? referenceCounter, IEnumerable? fields = null) : base(referenceCounter, fields) { } diff --git a/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs index 2fed4d32fb..5fc1ba4381 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/AndCondition.cs @@ -67,7 +67,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(new VM.Types.Array(referenceCounter, Expressions.Select(p => p.ToStackItem(referenceCounter)))); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs index e7609fcbaf..011a7466fe 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/BooleanCondition.cs @@ -55,7 +55,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(Expression); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs index f853743b72..b27c66931d 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs @@ -55,7 +55,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(Hash.ToArray()); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs index d28cd9f8bf..652eabd105 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs @@ -60,7 +60,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(Group.ToArray()); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs index 3187b62988..be26fc0863 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs @@ -60,7 +60,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(Group.ToArray()); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs index 83ecd4d973..74ab01d21b 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs @@ -61,7 +61,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(Expression.ToStackItem(referenceCounter)); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs index 72b27529c4..c28d3dbb94 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/OrCondition.cs @@ -67,7 +67,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(new VM.Types.Array(referenceCounter, Expressions.Select(p => p.ToStackItem(referenceCounter)))); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs index 9199e1a9a2..5d648efbc0 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs @@ -55,7 +55,7 @@ public override JObject ToJson() return json; } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { var result = (VM.Types.Array)base.ToStackItem(referenceCounter); result.Add(Hash.ToArray()); diff --git a/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs index 7738a6d4a8..dd955fce42 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/WitnessCondition.cs @@ -127,7 +127,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) throw new NotSupportedException(); } - public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + public virtual StackItem ToStackItem(IReferenceCounter referenceCounter) { return new VM.Types.Array(referenceCounter, new StackItem[] { (byte)Type }); } diff --git a/src/Neo/Network/P2P/Payloads/Signer.cs b/src/Neo/Network/P2P/Payloads/Signer.cs index 9b5e317abc..40496dfe74 100644 --- a/src/Neo/Network/P2P/Payloads/Signer.cs +++ b/src/Neo/Network/P2P/Payloads/Signer.cs @@ -191,7 +191,7 @@ void IInteroperable.FromStackItem(VM.Types.StackItem stackItem) throw new NotSupportedException(); } - VM.Types.StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + VM.Types.StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter) { return new VM.Types.Array(referenceCounter, [ diff --git a/src/Neo/Network/P2P/Payloads/Transaction.cs b/src/Neo/Network/P2P/Payloads/Transaction.cs index 6f41b5141b..b5139f1c7c 100644 --- a/src/Neo/Network/P2P/Payloads/Transaction.cs +++ b/src/Neo/Network/P2P/Payloads/Transaction.cs @@ -459,7 +459,7 @@ public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) return VerifyResult.Succeed; } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { if (_signers == null || _signers.Length == 0) throw new ArgumentException("Sender is not specified in the transaction."); return new Array(referenceCounter, new StackItem[] diff --git a/src/Neo/Network/P2P/Payloads/WitnessRule.cs b/src/Neo/Network/P2P/Payloads/WitnessRule.cs index 3bac09e7f5..8552f0798e 100644 --- a/src/Neo/Network/P2P/Payloads/WitnessRule.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRule.cs @@ -88,7 +88,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) throw new NotSupportedException(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new VM.Types.Array(referenceCounter, new StackItem[] { diff --git a/src/Neo/SmartContract/BinarySerializer.cs b/src/Neo/SmartContract/BinarySerializer.cs index 2bdeced678..b77a998d6e 100644 --- a/src/Neo/SmartContract/BinarySerializer.cs +++ b/src/Neo/SmartContract/BinarySerializer.cs @@ -51,9 +51,9 @@ public ContainerPlaceholder(StackItemType type, int count) /// /// The byte array to parse. /// The limits for the deserialization. - /// The used by the . + /// The used by the . /// The deserialized . - public static StackItem Deserialize(ReadOnlyMemory data, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ReadOnlyMemory data, ExecutionEngineLimits limits, IReferenceCounter referenceCounter = null) { MemoryReader reader = new(data); return Deserialize(ref reader, (uint)Math.Min(data.Length, limits.MaxItemSize), limits.MaxStackSize, referenceCounter); @@ -64,9 +64,9 @@ public static StackItem Deserialize(ReadOnlyMemory data, ExecutionEngineLi /// /// The for reading data. /// The limits for the deserialization. - /// The used by the . + /// The used by the . /// The deserialized . - public static StackItem Deserialize(ref MemoryReader reader, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ref MemoryReader reader, ExecutionEngineLimits limits, IReferenceCounter referenceCounter = null) { return Deserialize(ref reader, limits.MaxItemSize, limits.MaxStackSize, referenceCounter); } @@ -77,9 +77,9 @@ public static StackItem Deserialize(ref MemoryReader reader, ExecutionEngineLimi /// The for reading data. /// The maximum size of the result. /// The max of items to serialize - /// The used by the . + /// The used by the . /// The deserialized . - public static StackItem Deserialize(ref MemoryReader reader, uint maxSize, uint maxItems, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ref MemoryReader reader, uint maxSize, uint maxItems, IReferenceCounter referenceCounter = null) { Stack deserialized = new(); int undeserialized = 1; diff --git a/src/Neo/SmartContract/ContractState.cs b/src/Neo/SmartContract/ContractState.cs index 5b413c41a4..cd065cb8c3 100644 --- a/src/Neo/SmartContract/ContractState.cs +++ b/src/Neo/SmartContract/ContractState.cs @@ -119,7 +119,7 @@ public JObject ToJson() }; } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Nef.ToArray(), Manifest.ToStackItem(referenceCounter) }); } diff --git a/src/Neo/SmartContract/IInteroperable.cs b/src/Neo/SmartContract/IInteroperable.cs index 2810d40d01..f177b3f76f 100644 --- a/src/Neo/SmartContract/IInteroperable.cs +++ b/src/Neo/SmartContract/IInteroperable.cs @@ -29,9 +29,9 @@ public interface IInteroperable /// /// Convert the current object to a . /// - /// The used by the . + /// The used by the . /// The converted . - StackItem ToStackItem(ReferenceCounter referenceCounter); + StackItem ToStackItem(IReferenceCounter referenceCounter); public IInteroperable Clone() { diff --git a/src/Neo/SmartContract/Iterators/IIterator.cs b/src/Neo/SmartContract/Iterators/IIterator.cs index 78c42f1abd..4f06182fe1 100644 --- a/src/Neo/SmartContract/Iterators/IIterator.cs +++ b/src/Neo/SmartContract/Iterators/IIterator.cs @@ -30,6 +30,6 @@ public interface IIterator : IDisposable /// Gets the element in the collection at the current position of the iterator. /// /// The element in the collection at the current position of the iterator. - StackItem Value(ReferenceCounter referenceCounter); + StackItem Value(IReferenceCounter referenceCounter); } } diff --git a/src/Neo/SmartContract/Iterators/StorageIterator.cs b/src/Neo/SmartContract/Iterators/StorageIterator.cs index 397333ad7c..b66998c9dc 100644 --- a/src/Neo/SmartContract/Iterators/StorageIterator.cs +++ b/src/Neo/SmartContract/Iterators/StorageIterator.cs @@ -39,7 +39,7 @@ public bool Next() return enumerator.MoveNext(); } - public StackItem Value(ReferenceCounter referenceCounter) + public StackItem Value(IReferenceCounter referenceCounter) { ReadOnlyMemory key = enumerator.Current.Key.Key; ReadOnlyMemory value = enumerator.Current.Value.Value; diff --git a/src/Neo/SmartContract/JsonSerializer.cs b/src/Neo/SmartContract/JsonSerializer.cs index 4d77104a6e..d2055be7d8 100644 --- a/src/Neo/SmartContract/JsonSerializer.cs +++ b/src/Neo/SmartContract/JsonSerializer.cs @@ -163,15 +163,15 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) /// The used. /// The to deserialize. /// The limits for the deserialization. - /// The used by the . + /// The used by the . /// The deserialized . - public static StackItem Deserialize(ApplicationEngine engine, JToken json, ExecutionEngineLimits limits, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(ApplicationEngine engine, JToken json, ExecutionEngineLimits limits, IReferenceCounter referenceCounter = null) { uint maxStackSize = limits.MaxStackSize; return Deserialize(engine, json, ref maxStackSize, referenceCounter); } - private static StackItem Deserialize(ApplicationEngine engine, JToken json, ref uint maxStackSize, ReferenceCounter referenceCounter) + private static StackItem Deserialize(ApplicationEngine engine, JToken json, ref uint maxStackSize, IReferenceCounter referenceCounter) { if (maxStackSize-- == 0) throw new FormatException(); switch (json) diff --git a/src/Neo/SmartContract/Manifest/ContractAbi.cs b/src/Neo/SmartContract/Manifest/ContractAbi.cs index cedd431d62..4517ffeed2 100644 --- a/src/Neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/Neo/SmartContract/Manifest/ContractAbi.cs @@ -44,7 +44,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Events = ((Array)@struct[1]).Select(p => p.ToInteroperable()).ToArray(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { diff --git a/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs index 90227dd78d..de77597655 100644 --- a/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -40,7 +40,7 @@ public virtual void FromStackItem(StackItem stackItem) Parameters = ((Array)@struct[1]).Select(p => p.ToInteroperable()).ToArray(); } - public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + public virtual StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { diff --git a/src/Neo/SmartContract/Manifest/ContractGroup.cs b/src/Neo/SmartContract/Manifest/ContractGroup.cs index 231354327c..8796c0f07a 100644 --- a/src/Neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/Neo/SmartContract/Manifest/ContractGroup.cs @@ -43,7 +43,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Signature = @struct[1].GetSpan().ToArray(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { PubKey.ToArray(), Signature }; } diff --git a/src/Neo/SmartContract/Manifest/ContractManifest.cs b/src/Neo/SmartContract/Manifest/ContractManifest.cs index b1bd317e41..17722eb48a 100644 --- a/src/Neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/Neo/SmartContract/Manifest/ContractManifest.cs @@ -88,7 +88,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Extra = (JObject)JToken.Parse(@struct[7].GetSpan()); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { diff --git a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs index cc8d0a16d5..56b36163de 100644 --- a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -47,7 +47,7 @@ public override void FromStackItem(StackItem stackItem) Safe = @struct[4].GetBoolean(); } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { Struct @struct = (Struct)base.ToStackItem(referenceCounter); @struct.Add((byte)ReturnType); diff --git a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs index 61906558d0..b36fa353fe 100644 --- a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -38,7 +38,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Type = (ContractParameterType)(byte)@struct[1].GetInteger(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { Name, (byte)Type }; } diff --git a/src/Neo/SmartContract/Manifest/ContractPermission.cs b/src/Neo/SmartContract/Manifest/ContractPermission.cs index 44c67fd9a1..cf4d5078c5 100644 --- a/src/Neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/Neo/SmartContract/Manifest/ContractPermission.cs @@ -68,7 +68,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) }; } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { diff --git a/src/Neo/SmartContract/Native/AccountState.cs b/src/Neo/SmartContract/Native/AccountState.cs index 031b37f8eb..4e8944ab73 100644 --- a/src/Neo/SmartContract/Native/AccountState.cs +++ b/src/Neo/SmartContract/Native/AccountState.cs @@ -30,7 +30,7 @@ public virtual void FromStackItem(StackItem stackItem) Balance = ((Struct)stackItem)[0].GetInteger(); } - public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + public virtual StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { Balance }; } diff --git a/src/Neo/SmartContract/Native/HashIndexState.cs b/src/Neo/SmartContract/Native/HashIndexState.cs index 229458ffdd..4ab7a991cf 100644 --- a/src/Neo/SmartContract/Native/HashIndexState.cs +++ b/src/Neo/SmartContract/Native/HashIndexState.cs @@ -27,7 +27,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) Index = (uint)@struct[1].GetInteger(); } - StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { Hash.ToArray(), Index }; } diff --git a/src/Neo/SmartContract/Native/InteroperableList.cs b/src/Neo/SmartContract/Native/InteroperableList.cs index e118db7621..09088f6156 100644 --- a/src/Neo/SmartContract/Native/InteroperableList.cs +++ b/src/Neo/SmartContract/Native/InteroperableList.cs @@ -40,7 +40,7 @@ abstract class InteroperableList : IList, IInteroperable public void Sort() => List.Sort(); protected abstract T ElementFromStackItem(StackItem item); - protected abstract StackItem ElementToStackItem(T element, ReferenceCounter referenceCounter); + protected abstract StackItem ElementToStackItem(T element, IReferenceCounter referenceCounter); public void FromStackItem(StackItem stackItem) { @@ -51,7 +51,7 @@ public void FromStackItem(StackItem stackItem) } } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Array(referenceCounter, this.Select(p => ElementToStackItem(p, referenceCounter))); } diff --git a/src/Neo/SmartContract/Native/NeoToken.cs b/src/Neo/SmartContract/Native/NeoToken.cs index d639a55caf..85309b4246 100644 --- a/src/Neo/SmartContract/Native/NeoToken.cs +++ b/src/Neo/SmartContract/Native/NeoToken.cs @@ -581,7 +581,7 @@ public override void FromStackItem(StackItem stackItem) LastGasPerVote = @struct[3].GetInteger(); } - public override StackItem ToStackItem(ReferenceCounter referenceCounter) + public override StackItem ToStackItem(IReferenceCounter referenceCounter) { Struct @struct = (Struct)base.ToStackItem(referenceCounter); @struct.Add(BalanceHeight); @@ -603,7 +603,7 @@ public void FromStackItem(StackItem stackItem) Votes = @struct[1].GetInteger(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { Registered, Votes }; } @@ -620,7 +620,7 @@ protected override (ECPoint, BigInteger) ElementFromStackItem(StackItem item) return (ECPoint.DecodePoint(@struct[0].GetSpan(), ECCurve.Secp256r1), @struct[1].GetInteger()); } - protected override StackItem ElementToStackItem((ECPoint PublicKey, BigInteger Votes) element, ReferenceCounter referenceCounter) + protected override StackItem ElementToStackItem((ECPoint PublicKey, BigInteger Votes) element, IReferenceCounter referenceCounter) { return new Struct(referenceCounter) { element.PublicKey.ToArray(), element.Votes }; } diff --git a/src/Neo/SmartContract/Native/OracleContract.cs b/src/Neo/SmartContract/Native/OracleContract.cs index be4765581a..54156a0029 100644 --- a/src/Neo/SmartContract/Native/OracleContract.cs +++ b/src/Neo/SmartContract/Native/OracleContract.cs @@ -241,7 +241,7 @@ protected override ulong ElementFromStackItem(StackItem item) return (ulong)item.GetInteger(); } - protected override StackItem ElementToStackItem(ulong element, ReferenceCounter referenceCounter) + protected override StackItem ElementToStackItem(ulong element, IReferenceCounter referenceCounter) { return element; } diff --git a/src/Neo/SmartContract/Native/OracleRequest.cs b/src/Neo/SmartContract/Native/OracleRequest.cs index d18968ef00..cd4962b1cb 100644 --- a/src/Neo/SmartContract/Native/OracleRequest.cs +++ b/src/Neo/SmartContract/Native/OracleRequest.cs @@ -68,7 +68,7 @@ public void FromStackItem(StackItem stackItem) UserData = array[6].GetSpan().ToArray(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Array(referenceCounter) { diff --git a/src/Neo/SmartContract/Native/RoleManagement.cs b/src/Neo/SmartContract/Native/RoleManagement.cs index d0437594c9..e57f3f3e83 100644 --- a/src/Neo/SmartContract/Native/RoleManagement.cs +++ b/src/Neo/SmartContract/Native/RoleManagement.cs @@ -79,7 +79,7 @@ protected override ECPoint ElementFromStackItem(StackItem item) return ECPoint.DecodePoint(item.GetSpan(), ECCurve.Secp256r1); } - protected override StackItem ElementToStackItem(ECPoint element, ReferenceCounter referenceCounter) + protected override StackItem ElementToStackItem(ECPoint element, IReferenceCounter referenceCounter) { return element.ToArray(); } diff --git a/src/Neo/SmartContract/Native/TransactionState.cs b/src/Neo/SmartContract/Native/TransactionState.cs index 050358f179..9e3eb2527a 100644 --- a/src/Neo/SmartContract/Native/TransactionState.cs +++ b/src/Neo/SmartContract/Native/TransactionState.cs @@ -76,7 +76,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) State = (VMState)(byte)@struct[2].GetInteger(); } - StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter) { if (Transaction is null) return new Struct(referenceCounter) { BlockIndex }; diff --git a/src/Neo/SmartContract/Native/TrimmedBlock.cs b/src/Neo/SmartContract/Native/TrimmedBlock.cs index c2bab2567c..69dd55fdbb 100644 --- a/src/Neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/Neo/SmartContract/Native/TrimmedBlock.cs @@ -80,7 +80,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) throw new NotSupportedException(); } - StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + StackItem IInteroperable.ToStackItem(IReferenceCounter referenceCounter) { return new VM.Types.Array(referenceCounter, new StackItem[] { diff --git a/src/Neo/SmartContract/NotifyEventArgs.cs b/src/Neo/SmartContract/NotifyEventArgs.cs index 257efb3a66..8d8542bca9 100644 --- a/src/Neo/SmartContract/NotifyEventArgs.cs +++ b/src/Neo/SmartContract/NotifyEventArgs.cs @@ -63,7 +63,7 @@ public void FromStackItem(StackItem stackItem) throw new NotSupportedException(); } - public StackItem ToStackItem(ReferenceCounter referenceCounter) + public StackItem ToStackItem(IReferenceCounter referenceCounter) { return new Array(referenceCounter) { @@ -73,7 +73,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) }; } - public StackItem ToStackItem(ReferenceCounter referenceCounter, ApplicationEngine engine) + public StackItem ToStackItem(IReferenceCounter referenceCounter, ApplicationEngine engine) { if (engine.IsHardforkEnabled(Hardfork.HF_Domovoi)) { From a32693924288a606e93d9a1f1f1fb19698bd292e Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 12 Oct 2024 02:16:57 +0800 Subject: [PATCH 11/53] [Neo VM] optimize newstruct (#3525) * optimize newstruct * use Array.Fill * Update src/Neo.VM/JumpTable/JumpTable.Compound.cs --------- Co-authored-by: Shargon --- src/Neo.VM/JumpTable/JumpTable.Compound.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Neo.VM/JumpTable/JumpTable.Compound.cs b/src/Neo.VM/JumpTable/JumpTable.Compound.cs index 817b460cf9..2a81b213fe 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Compound.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Compound.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; +using Array = System.Array; using VMArray = Neo.VM.Types.Array; namespace Neo.VM @@ -151,8 +152,9 @@ 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}"); - - engine.Push(new VMArray(engine.ReferenceCounter, Enumerable.Repeat(StackItem.Null, n))); + var nullArray = new StackItem[n]; + Array.Fill(nullArray, StackItem.Null); + engine.Push(new VMArray(engine.ReferenceCounter, nullArray)); } /// @@ -180,8 +182,9 @@ public virtual void NewArray_T(ExecutionEngine engine, Instruction instruction) (byte)StackItemType.ByteString => ByteString.Empty, _ => StackItem.Null }; - - engine.Push(new VMArray(engine.ReferenceCounter, Enumerable.Repeat(item, n))); + var itemArray = new StackItem[n]; + Array.Fill(itemArray, item); + engine.Push(new VMArray(engine.ReferenceCounter, itemArray)); } /// @@ -210,10 +213,10 @@ 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}"); - Struct result = new(engine.ReferenceCounter); - for (var i = 0; i < n; i++) - result.Add(StackItem.Null); - engine.Push(result); + + var nullArray = new StackItem[n]; + Array.Fill(nullArray, StackItem.Null); + engine.Push(new Struct(engine.ReferenceCounter, nullArray)); } /// From b752efe926dd0eae136ad4694e22daa057a03790 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Mon, 14 Oct 2024 15:49:50 +0800 Subject: [PATCH 12/53] fea: use canonical TryGet style in IReadOnlyStore (#3533) --- src/Neo/Persistence/IReadOnlyStore.cs | 10 ++++++++++ src/Neo/Persistence/MemorySnapshot.cs | 5 +++++ src/Neo/Persistence/MemoryStore.cs | 6 ++++++ .../LevelDBStore/Plugins/Storage/Snapshot.cs | 6 ++++++ .../LevelDBStore/Plugins/Storage/Store.cs | 6 ++++++ .../RocksDBStore/Plugins/Storage/Snapshot.cs | 6 ++++++ .../RocksDBStore/Plugins/Storage/Store.cs | 6 ++++++ .../Cryptography/MPTTrie/UT_Trie.cs | 5 +++++ tests/Neo.Plugins.Storage.Tests/StoreTest.cs | 19 +++++++++++++++++++ .../Persistence/UT_MemorySnapshot.cs | 8 ++++++++ .../Persistence/UT_MemoryStore.cs | 10 +++++++++- 11 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/Neo/Persistence/IReadOnlyStore.cs b/src/Neo/Persistence/IReadOnlyStore.cs index 4b6c3fe0ec..e52ccbb7c3 100644 --- a/src/Neo/Persistence/IReadOnlyStore.cs +++ b/src/Neo/Persistence/IReadOnlyStore.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using System; using System.Collections.Generic; namespace Neo.Persistence @@ -31,8 +32,17 @@ public interface IReadOnlyStore /// /// The key of the entry. /// The data of the entry. Or if it doesn't exist. + /// . Obsolete it later for avoiding complier warning. byte[] TryGet(byte[] key); + /// + /// Reads a specified entry from the database. + /// + /// The key of the entry. + /// The data of the entry. + /// if the entry exists; otherwise, . + bool TryGet(byte[] key, out byte[] value); + /// /// Determines whether the database contains the specified entry. /// diff --git a/src/Neo/Persistence/MemorySnapshot.cs b/src/Neo/Persistence/MemorySnapshot.cs index 096431552c..b3d06798d4 100644 --- a/src/Neo/Persistence/MemorySnapshot.cs +++ b/src/Neo/Persistence/MemorySnapshot.cs @@ -69,6 +69,11 @@ public byte[] TryGet(byte[] key) return value?[..]; } + public bool TryGet(byte[] key, out byte[] value) + { + return immutableData.TryGetValue(key, out value); + } + public bool Contains(byte[] key) { return immutableData.ContainsKey(key); diff --git a/src/Neo/Persistence/MemoryStore.cs b/src/Neo/Persistence/MemoryStore.cs index 191b89ea4f..378d5374f1 100644 --- a/src/Neo/Persistence/MemoryStore.cs +++ b/src/Neo/Persistence/MemoryStore.cs @@ -66,6 +66,12 @@ public byte[] TryGet(byte[] key) return value[..]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGet(byte[] key, out byte[] value) + { + return _innerData.TryGetValue(key, out value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(byte[] key) { diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index 0b0a63b885..f66164285b 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -65,5 +65,11 @@ public byte[] TryGet(byte[] key) { return db.Get(options, key); } + + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(options, key); + return value != null; + } } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs index 27b12a8b64..175b71a8fe 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -63,5 +63,11 @@ public byte[] TryGet(byte[] key) { return db.Get(ReadOptions.Default, key); } + + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(ReadOptions.Default, key); + return value != null; + } } } diff --git a/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs index 7423f6ae4a..4da29e41fe 100644 --- a/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs @@ -73,6 +73,12 @@ public byte[] TryGet(byte[] key) return db.Get(key, readOptions: options); } + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(key, readOptions: options); + return value != null; + } + public void Dispose() { snapshot.Dispose(); diff --git a/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs b/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs index ebf160dab0..3f8121ca07 100644 --- a/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs @@ -59,6 +59,12 @@ public byte[] TryGet(byte[] key) return db.Get(key); } + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(key); + return value != null; + } + public void Delete(byte[] key) { db.Remove(key); diff --git a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs index 2e53456545..d7fcb92775 100644 --- a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs +++ b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs @@ -52,6 +52,11 @@ public byte[] TryGet(byte[] key) return null; } + public bool TryGet(byte[] key, out byte[] value) + { + return store.TryGetValue(StoreKey(key), out value); + } + public void Dispose() { throw new System.NotImplementedException(); } public int Size => store.Count; diff --git a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs index 189e212bc2..ed44faab53 100644 --- a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs +++ b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs @@ -85,6 +85,8 @@ public void TestLevelDbSnapshot() snapshot.Put(testKey, testValue); // Data saved to the leveldb snapshot shall not be visible to the store Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out var got)); + Assert.IsNull(got); // Value is in the write batch, not visible to the store and snapshot Assert.AreEqual(false, snapshot.Contains(testKey)); @@ -94,7 +96,13 @@ public void TestLevelDbSnapshot() // After commit, the data shall be visible to the store but not to the snapshot Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out got)); + Assert.IsNull(got); + CollectionAssert.AreEqual(testValue, store.TryGet(testKey)); + Assert.IsTrue(store.TryGet(testKey, out got)); + CollectionAssert.AreEqual(testValue, got); + Assert.AreEqual(false, snapshot.Contains(testKey)); Assert.AreEqual(true, store.Contains(testKey)); @@ -154,7 +162,12 @@ public void TestRocksDbSnapshot() snapshot.Put(testKey, testValue); // Data saved to the leveldb snapshot shall not be visible Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out var got)); + Assert.IsNull(got); + Assert.IsNull(store.TryGet(testKey)); + Assert.IsFalse(store.TryGet(testKey, out got)); + Assert.IsNull(got); // Value is in the write batch, not visible to the store and snapshot Assert.AreEqual(false, snapshot.Contains(testKey)); @@ -164,7 +177,13 @@ public void TestRocksDbSnapshot() // After commit, the data shall be visible to the store but not to the snapshot Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out got)); + Assert.IsNull(got); + CollectionAssert.AreEqual(testValue, store.TryGet(testKey)); + Assert.IsTrue(store.TryGet(testKey, out got)); + CollectionAssert.AreEqual(testValue, got); + Assert.AreEqual(false, snapshot.Contains(testKey)); Assert.AreEqual(true, store.Contains(testKey)); diff --git a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs index 629e2bc374..8eea33c9a7 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs @@ -97,6 +97,14 @@ public void MultiSnapshotTest() 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 diff --git a/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs index 133d9eb66c..4473ae63a8 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs @@ -51,6 +51,9 @@ public void StoreTest() store.Delete([1]); Assert.AreEqual(null, store.TryGet([1])); + Assert.IsFalse(store.TryGet([1], out var got)); + Assert.AreEqual(null, got); + store.Put([1], [1, 2, 3]); CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, store.TryGet([1])); @@ -79,13 +82,18 @@ public void NeoSystemStoreViewTest() var store = _neoSystem.StoreView; var key = new StorageKey(Encoding.UTF8.GetBytes("testKey")); var value = new StorageItem(Encoding.UTF8.GetBytes("testValue")); + store.Add(key, value); store.Commit(); + var result = store.TryGet(key); // The StoreView is a readonly view of the store, here it will have value in the cache Assert.AreEqual("testValue", Encoding.UTF8.GetString(result.Value.ToArray())); - // But the value will not be written to the underlying store even its committed. + + // But the value will not be written to the underlying store even its committed. Assert.IsNull(_memoryStore.TryGet(key.ToArray())); + Assert.IsFalse(_memoryStore.TryGet(key.ToArray(), out var got)); + Assert.AreEqual(null, got); } [TestMethod] From 3f2f78c1fcf71ea51f2c40c3a0e67062ac390aed Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 14 Oct 2024 10:23:05 +0200 Subject: [PATCH 13/53] Add references (#3529) Co-authored-by: Jimmy --- benchmarks/Neo.VM.Benchmarks/TestArray.cs | 4 ++-- benchmarks/Neo.VM.Benchmarks/TestStruct.cs | 4 ++-- src/Neo.VM/ExecutionEngine.cs | 5 +++-- src/Neo.VM/Types/Array.cs | 4 ++-- src/Neo.VM/Types/Struct.cs | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/benchmarks/Neo.VM.Benchmarks/TestArray.cs b/benchmarks/Neo.VM.Benchmarks/TestArray.cs index 6a7dbcabc1..0799040e52 100644 --- a/benchmarks/Neo.VM.Benchmarks/TestArray.cs +++ b/benchmarks/Neo.VM.Benchmarks/TestArray.cs @@ -53,9 +53,9 @@ public TestArray(IEnumerable? items = null) } /// - /// Create an array containing the specified items. And make the array use the specified . + /// Create an array containing the specified items. And make the array use the specified . /// - /// The to be used by this array. + /// The to be used by this array. /// The items to be included in the array. public TestArray(IReferenceCounter? referenceCounter, IEnumerable? items = null) : base(referenceCounter) diff --git a/benchmarks/Neo.VM.Benchmarks/TestStruct.cs b/benchmarks/Neo.VM.Benchmarks/TestStruct.cs index 8f77d680c5..f7dc2fcb64 100644 --- a/benchmarks/Neo.VM.Benchmarks/TestStruct.cs +++ b/benchmarks/Neo.VM.Benchmarks/TestStruct.cs @@ -27,9 +27,9 @@ public TestStruct(IEnumerable? fields = null) } /// - /// Create a structure with the specified fields. And make the structure use the specified . + /// Create a structure with the specified fields. And make the structure use the specified . /// - /// The to be used by this structure. + /// 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) diff --git a/src/Neo.VM/ExecutionEngine.cs b/src/Neo.VM/ExecutionEngine.cs index b2c04bdc93..8a3167b4c8 100644 --- a/src/Neo.VM/ExecutionEngine.cs +++ b/src/Neo.VM/ExecutionEngine.cs @@ -83,17 +83,18 @@ protected internal set /// /// Initializes a new instance of the class. /// + /// The jump table to be used. public ExecutionEngine(JumpTable? jumpTable = null) : this(jumpTable, new ReferenceCounter(), ExecutionEngineLimits.Default) { } /// - /// Initializes a new instance of the class with the specified and . + /// Initializes a new instance of the class with the specified and . /// /// The jump table to be used. /// The reference counter to be used. /// Restrictions on the VM. - protected ExecutionEngine(JumpTable? jumpTable, ReferenceCounter referenceCounter, ExecutionEngineLimits limits) + protected ExecutionEngine(JumpTable? jumpTable, IReferenceCounter referenceCounter, ExecutionEngineLimits limits) { JumpTable = jumpTable ?? JumpTable.Default; Limits = limits; diff --git a/src/Neo.VM/Types/Array.cs b/src/Neo.VM/Types/Array.cs index 831bc830e6..903613c228 100644 --- a/src/Neo.VM/Types/Array.cs +++ b/src/Neo.VM/Types/Array.cs @@ -62,9 +62,9 @@ public Array(IEnumerable? items = null) } /// - /// Create an array containing the specified items. And make the array use the specified . + /// Create an array containing the specified items. And make the array use the specified . /// - /// The to be used by this array. + /// The to be used by this array. /// The items to be included in the array. public Array(IReferenceCounter? referenceCounter, IEnumerable? items = null) : base(referenceCounter) diff --git a/src/Neo.VM/Types/Struct.cs b/src/Neo.VM/Types/Struct.cs index f2ae144d3e..344147b5ed 100644 --- a/src/Neo.VM/Types/Struct.cs +++ b/src/Neo.VM/Types/Struct.cs @@ -31,9 +31,9 @@ public Struct(IEnumerable? fields = null) } /// - /// Create a structure with the specified fields. And make the structure use the specified . + /// Create a structure with the specified fields. And make the structure use the specified . /// - /// The to be used by this structure. + /// The to be used by this structure. /// The fields to be included in the structure. public Struct(IReferenceCounter? referenceCounter, IEnumerable? fields = null) : base(referenceCounter, fields) From bb71676fd9013e8cda8499ef181c59e7514e7f8c Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 15 Oct 2024 01:50:38 +0800 Subject: [PATCH 14/53] fix: concurrency conflict in NEP6Wallet.ToJson (#3527) * fix: concurrency conflict in NEP6Wallet.ToJson * Update also ChangePasssword * Reduce lock time --------- Co-authored-by: Shargon Co-authored-by: Jimmy --- src/Neo/Wallets/NEP6/NEP6Wallet.cs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Neo/Wallets/NEP6/NEP6Wallet.cs b/src/Neo/Wallets/NEP6/NEP6Wallet.cs index 3edb41c5aa..c205637a97 100644 --- a/src/Neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/Neo/Wallets/NEP6/NEP6Wallet.cs @@ -293,12 +293,18 @@ public override WalletAccount Import(string nep2, string passphrase, int N = 163 /// public JObject ToJson() { + NEP6Account[] accountValues; + lock (accounts) + { + accountValues = accounts.Values.ToArray(); + } + return new() { ["name"] = name, ["version"] = version.ToString(), ["scrypt"] = Scrypt.ToJson(), - ["accounts"] = accounts.Values.Select(p => p.ToJson()).ToArray(), + ["accounts"] = accountValues.Select(p => p.ToJson()).ToArray(), ["extra"] = extra }; } @@ -345,26 +351,28 @@ private bool VerifyPasswordInternal(string password) public override bool ChangePassword(string oldPassword, string newPassword) { bool succeed = true; + NEP6Account[] accountsValues; lock (accounts) { - Parallel.ForEach(accounts.Values, (account, state) => - { - if (!account.ChangePasswordPrepare(oldPassword, newPassword)) - { - state.Stop(); - succeed = false; - } - }); + accountsValues = accounts.Values.ToArray(); } + Parallel.ForEach(accountsValues, (account, state) => + { + if (!account.ChangePasswordPrepare(oldPassword, newPassword)) + { + state.Stop(); + succeed = false; + } + }); if (succeed) { - foreach (NEP6Account account in accounts.Values) + foreach (NEP6Account account in accountsValues) account.ChangePasswordCommit(); password = newPassword.ToSecureString(); } else { - foreach (NEP6Account account in accounts.Values) + foreach (NEP6Account account in accountsValues) account.ChangePasswordRollback(); } return succeed; From 16bde11a3abcafd2b6ad745103ab42726a49643f Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Mon, 21 Oct 2024 23:47:03 +0800 Subject: [PATCH 15/53] fix cref "OpCode.SUBSTR" in comment (#3542) --- src/Neo.VM/JumpTable/JumpTable.Splice.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo.VM/JumpTable/JumpTable.Splice.cs b/src/Neo.VM/JumpTable/JumpTable.Splice.cs index f04d045988..eaa979a8b8 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Splice.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Splice.cs @@ -85,7 +85,7 @@ public virtual void Cat(ExecutionEngine engine, Instruction instruction) /// /// Extracts a substring from the specified buffer and pushes it onto the evaluation stack. - /// + /// /// /// The execution engine. /// The instruction being executed. From 6d7ea43b2ea6bc3cffb8a417755c482eaaa66241 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 23 Oct 2024 17:16:32 +0800 Subject: [PATCH 16/53] ApplicationEngine helper to get engine error info (#3541) * helper to get engine error info * cancel try * Update src/Neo/SmartContract/ApplicationEngine.Helper.cs Co-authored-by: Shargon * standalone method to get exception stack trace and message * always return string --------- Co-authored-by: Jimmy Co-authored-by: Shargon --- .../SmartContract/ApplicationEngine.Helper.cs | 56 +++++++++++++++++++ .../SmartContract/UT_ApplicationEngine.cs | 7 +++ 2 files changed, 63 insertions(+) create mode 100644 src/Neo/SmartContract/ApplicationEngine.Helper.cs diff --git a/src/Neo/SmartContract/ApplicationEngine.Helper.cs b/src/Neo/SmartContract/ApplicationEngine.Helper.cs new file mode 100644 index 0000000000..5d2976c288 --- /dev/null +++ b/src/Neo/SmartContract/ApplicationEngine.Helper.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ApplicationEngine.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 Neo.SmartContract.Native; +using Neo.VM; +using System; +using System.Linq; +using System.Text; + +namespace Neo.SmartContract +{ + public partial class ApplicationEngine : ExecutionEngine + { + public string GetEngineStackInfoOnFault(bool exceptionStackTrace = true, bool exceptionMessage = true) + { + if (State != VMState.FAULT || FaultException == null) + return ""; + StringBuilder traceback = new(); + if (CallingScriptHash != null) + traceback.AppendLine($"CallingScriptHash={CallingScriptHash}[{NativeContract.ContractManagement.GetContract(SnapshotCache, CallingScriptHash)?.Manifest.Name}]"); + traceback.AppendLine($"CurrentScriptHash={CurrentScriptHash}[{NativeContract.ContractManagement.GetContract(SnapshotCache, CurrentScriptHash)?.Manifest.Name}]"); + traceback.AppendLine($"EntryScriptHash={EntryScriptHash}"); + + foreach (ExecutionContext context in InvocationStack.Reverse()) + { + UInt160 contextScriptHash = context.GetScriptHash(); + string contextContractName = NativeContract.ContractManagement.GetContract(SnapshotCache, contextScriptHash)?.Manifest.Name; + traceback.AppendLine($"\tInstructionPointer={context.InstructionPointer}, OpCode {context.CurrentInstruction?.OpCode}, Script Length={context.Script.Length} {contextScriptHash}[{contextContractName}]"); + } + traceback.Append(GetEngineExceptionInfo(exceptionStackTrace: exceptionStackTrace, exceptionMessage: exceptionMessage)); + + return traceback.ToString(); + } + + public string GetEngineExceptionInfo(bool exceptionStackTrace = true, bool exceptionMessage = true) + { + if (State != VMState.FAULT || FaultException == null) + return ""; + StringBuilder traceback = new(); + Exception baseException = FaultException.GetBaseException(); + if (exceptionStackTrace) + traceback.AppendLine(baseException.StackTrace); + if (exceptionMessage) + traceback.AppendLine(baseException.Message); + return traceback.ToString(); + } + } +} diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index a670e3b4b7..def978596e 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -167,8 +167,15 @@ public void TestSystem_Contract_Call_Permissions() }; var currentScriptHash = engine.EntryScriptHash; + Assert.AreEqual("", engine.GetEngineStackInfoOnFault()); Assert.AreEqual(VMState.FAULT, engine.Execute()); Assert.IsTrue(engine.FaultException.ToString().Contains($"Cannot Call Method disallowed Of Contract {scriptHash.ToString()}")); + string traceback = engine.GetEngineStackInfoOnFault(); + Assert.IsTrue(traceback.Contains($"Cannot Call Method disallowed Of Contract {scriptHash.ToString()}")); + Assert.IsTrue(traceback.Contains("CurrentScriptHash")); + Assert.IsTrue(traceback.Contains("EntryScriptHash")); + Assert.IsTrue(traceback.Contains("InstructionPointer")); + Assert.IsTrue(traceback.Contains("OpCode SYSCALL, Script Length=")); } // Allowed method call. From 85f52f5c132cd6585815a8b0fee8030d98a6970b Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Wed, 23 Oct 2024 17:57:08 +0800 Subject: [PATCH 17/53] stack opcode example comments (#3546) * stack opcode example comments * move index into example --------- Co-authored-by: Jimmy --- src/Neo.VM/OpCode.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Neo.VM/OpCode.cs b/src/Neo.VM/OpCode.cs index 8a2878473c..4a705a556c 100644 --- a/src/Neo.VM/OpCode.cs +++ b/src/Neo.VM/OpCode.cs @@ -725,6 +725,10 @@ public enum OpCode : byte /// /// The item n back in the stack is copied to the top. /// + /// a b c d 2 -> a b c d b + /// index => 3[2]1 0 + /// + /// /// /// Push: 1 item(s) /// Pop: 0 item(s) @@ -735,6 +739,8 @@ public enum OpCode : byte /// /// The item at the top of the stack is copied and inserted before the second-to-top item. /// + /// a b c -> a c b c + /// /// /// Push: 1 item(s) /// Pop: 0 item(s) @@ -769,6 +775,10 @@ public enum OpCode : byte /// /// The item n back in the stack is moved to the top. /// + /// a b c d 2 -> a c d b + /// index => 3[2]1 0 + /// + /// /// /// Push: 0 item(s) /// Pop: 1 item(s) @@ -793,7 +803,6 @@ public enum OpCode : byte /// /// a b c d -> d c b a /// - /// /// /// Push: 0 item(s) /// Pop: 0 item(s) @@ -804,7 +813,8 @@ public enum OpCode : byte /// /// Pop the number N on the stack, and reverse the order of the top N items on the stack. /// - /// + /// a b c d 3 -> a d c b + /// /// /// Push: 0 item(s) /// Pop: 1 item(s) From 9ed6cac3786ef598715537f650018c7a7da35cec Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 23 Oct 2024 12:08:51 +0200 Subject: [PATCH 18/53] Expose `GetInteropDescriptor` (#3545) * Expose GetInteropDescriptor * Update src/Neo/SmartContract/ApplicationEngine.cs --------- Co-authored-by: Jimmy Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo/SmartContract/ApplicationEngine.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.cs b/src/Neo/SmartContract/ApplicationEngine.cs index 38430f3632..7808baae5e 100644 --- a/src/Neo/SmartContract/ApplicationEngine.cs +++ b/src/Neo/SmartContract/ApplicationEngine.cs @@ -22,6 +22,7 @@ using System.Linq; using System.Numerics; using System.Reflection; +using System.Runtime.CompilerServices; using Array = System.Array; using VMArray = Neo.VM.Types.Array; @@ -241,9 +242,7 @@ protected static void OnSysCall(ExecutionEngine engine, Instruction instruction) { if (engine is ApplicationEngine app) { - uint method = instruction.TokenU32; - - app.OnSysCall(services[method]); + app.OnSysCall(GetInteropDescriptor(instruction.TokenU32)); } else { @@ -641,6 +640,17 @@ private static InteropDescriptor Register(string name, string handler, long fixe return descriptor; } + /// + /// Get Interop Descriptor + /// + /// Method Hash + /// InteropDescriptor + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static InteropDescriptor GetInteropDescriptor(uint methodHash) + { + return services[methodHash]; + } + /// /// Creates a new instance of the class, and use it to run the specified script. /// From 7f5504090420baf18175a485e959f268b0d11712 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 29 Oct 2024 16:05:12 +0800 Subject: [PATCH 19/53] fix: unexpected InvalidOperationException with CyclicReference in ContractParamter.ToJson (#3555) Co-authored-by: Christopher Schuchardt --- src/Neo/SmartContract/ContractParameter.cs | 2 ++ .../SmartContract/UT_ContractParameter.cs | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/Neo/SmartContract/ContractParameter.cs b/src/Neo/SmartContract/ContractParameter.cs index f1b12c6c0a..3a325d4520 100644 --- a/src/Neo/SmartContract/ContractParameter.cs +++ b/src/Neo/SmartContract/ContractParameter.cs @@ -168,6 +168,7 @@ private static JObject ToJson(ContractParameter parameter, HashSet)parameter.Value).Select(p => ToJson(p, context))); + context.Remove(parameter); break; case ContractParameterType.Map: if (context is null) @@ -182,6 +183,7 @@ private static JObject ToJson(ContractParameter parameter, HashSet(); } + [TestMethod] + public void TestContractParameterCyclicReference() + { + var map = new ContractParameter + { + Type = ContractParameterType.Map, + Value = new List> + { + new( + new ContractParameter { Type = ContractParameterType.Integer, Value = 1 }, + new ContractParameter { Type = ContractParameterType.Integer, Value = 2 } + ) + } + }; + + var value = new List { map, map }; + var item = new ContractParameter { Type = ContractParameterType.Array, Value = value }; + + // just check there is no exception + var json = item.ToJson(); + Assert.AreEqual(json.ToString(), ContractParameter.FromJson(json).ToJson().ToString()); + + // check cyclic reference + value.Add(item); + Action action = () => item.ToJson(); + action.Should().Throw(); + } + [TestMethod] public void TestSetValue() { From 03ba1dc1dc77c296b6e0b9dfe10a862f1d8659a3 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Wed, 30 Oct 2024 00:03:34 +0300 Subject: [PATCH 20/53] OpCodes: extend MODPOW tests with negative base/exp/mod (#3557) We need to ensure that NeoGo VM behaviour is compatible, ref. https://github.com/nspcc-dev/neo-go/pull/3649. Signed-off-by: Anna Shaleva Co-authored-by: Shargon --- .../Tests/OpCodes/Arithmetic/MODPOW.json | 344 ++++++++++++++++++ 1 file changed, 344 insertions(+) diff --git a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json index 68efa97efa..4551bb99b8 100644 --- a/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json +++ b/tests/Neo.VM.Tests/Tests/OpCodes/Arithmetic/MODPOW.json @@ -140,6 +140,350 @@ } } ] + }, + { + "name": "(3 ^ 4) % 5 == 1", + "script": [ + "PUSH3", + "PUSH4", + "PUSH5", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "(-1 ^ 3) % 3 == -1", + "script": [ + "PUSHM1", + "PUSH3", + "PUSH3", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": -1 + } + ] + } + } + ] + }, + { + "name": "(-1 ^ 3) % -3 == -1", + "script": [ + "PUSHM1", + "PUSH3", + "PUSH3", + "NEGATE", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": -3 + }, + { + "type": "Integer", + "value": 3 + }, + { + "type": "Integer", + "value": -1 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": -1 + } + ] + } + } + ] + }, + { + "name": "(-3 ^ 5) % -5 == -3", + "script": [ + "PUSH3", + "NEGATE", + "PUSH5", + "PUSH5", + "NEGATE", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 5, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": -5 + }, + { + "type": "Integer", + "value": 5 + }, + { + "type": "Integer", + "value": -3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": -3 + } + ] + } + } + ] + }, + { + "name": "(3 ^ 4) % -5 == 1", + "script": [ + "PUSH3", + "PUSH4", + "PUSH5", + "NEGATE", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 4, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": -5 + }, + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": 3 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] + }, + { + "name": "(5 ^ -1) % 4 == 1", + "script": [ + "PUSH5", + "PUSHM1", + "PUSH4", + "MODPOW" + ], + "steps": [ + { + "actions": [ + "stepInto", + "stepInto", + "stepInto" + ], + "result": { + "state": "BREAK", + "invocationStack": [ + { + "instructionPointer": 3, + "nextInstruction": "MODPOW", + "evaluationStack": [ + { + "type": "Integer", + "value": 4 + }, + { + "type": "Integer", + "value": -1 + }, + { + "type": "Integer", + "value": 5 + } + ] + } + ] + } + }, + { + "actions": [ + "execute" + ], + "result": { + "state": "HALT", + "invocationStack": [], + "resultStack": [ + { + "type": "Integer", + "value": 1 + } + ] + } + } + ] } ] } From a42bc8d1470fb06686b8dfa555901023f80b3124 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 30 Oct 2024 21:58:59 +0100 Subject: [PATCH 21/53] Fix InvalidOperationException (#3558) * Fix InvalidOperationException * use ! * Unify * fix --- src/Neo/SmartContract/ContractParameter.cs | 18 ++++++---------- src/Neo/VM/Helper.cs | 6 ++++-- tests/Neo.UnitTests/VM/UT_Helper.cs | 24 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/Neo/SmartContract/ContractParameter.cs b/src/Neo/SmartContract/ContractParameter.cs index 3a325d4520..001abeb663 100644 --- a/src/Neo/SmartContract/ContractParameter.cs +++ b/src/Neo/SmartContract/ContractParameter.cs @@ -162,20 +162,14 @@ private static JObject ToJson(ContractParameter parameter, HashSet(); - else if (context.Contains(parameter)) - throw new InvalidOperationException(); - context.Add(parameter); + context ??= []; + if (!context.Add(parameter)) throw new InvalidOperationException("Circular reference."); json["value"] = new JArray(((IList)parameter.Value).Select(p => ToJson(p, context))); - context.Remove(parameter); + if (!context.Remove(parameter)) throw new InvalidOperationException("Circular reference."); break; case ContractParameterType.Map: - if (context is null) - context = new HashSet(); - else if (context.Contains(parameter)) - throw new InvalidOperationException(); - context.Add(parameter); + context ??= []; + if (!context.Add(parameter)) throw new InvalidOperationException("Circular reference."); json["value"] = new JArray(((IList>)parameter.Value).Select(p => { JObject item = new(); @@ -183,7 +177,7 @@ private static JObject ToJson(ContractParameter parameter, HashSet context, ref in { case Array array: { - context ??= new HashSet(ReferenceEqualityComparer.Instance); + context ??= new(ReferenceEqualityComparer.Instance); if (!context.Add(array)) throw new InvalidOperationException("Circular reference."); maxSize -= 2/*[]*/+ Math.Max(0, (array.Count - 1))/*,*/; JArray a = new(); foreach (StackItem stackItem in array) a.Add(ToJson(stackItem, context, ref maxSize)); value = a; + if (!context.Remove(array)) throw new InvalidOperationException("Circular reference."); break; } case Boolean boolean: @@ -397,7 +398,7 @@ private static JObject ToJson(StackItem item, HashSet context, ref in } case Map map: { - context ??= new HashSet(ReferenceEqualityComparer.Instance); + context ??= new(ReferenceEqualityComparer.Instance); if (!context.Add(map)) throw new InvalidOperationException("Circular reference."); maxSize -= 2/*[]*/+ Math.Max(0, (map.Count - 1))/*,*/; JArray a = new(); @@ -412,6 +413,7 @@ private static JObject ToJson(StackItem item, HashSet context, ref in a.Add(i); } value = a; + if (!context.Remove(map)) throw new InvalidOperationException("Circular reference."); break; } case Pointer pointer: diff --git a/tests/Neo.UnitTests/VM/UT_Helper.cs b/tests/Neo.UnitTests/VM/UT_Helper.cs index db47555dbd..cabe0c7a02 100644 --- a/tests/Neo.UnitTests/VM/UT_Helper.cs +++ b/tests/Neo.UnitTests/VM/UT_Helper.cs @@ -689,5 +689,29 @@ public void TestCharAsUInt16() CollectionAssert.AreEqual(sbUInt16.ToArray(), sbChar.ToArray()); } } + + [TestMethod] + public void TestCyclicReference() + { + var map = new VM.Types.Map + { + [1] = 2, + }; + + var item = new VM.Types.Array + { + map, + map + }; + + // just check there is no exception + var json = item.ToJson(); + Assert.AreEqual(json.ToString(), @"{""type"":""Array"",""value"":[{""type"":""Map"",""value"":[{""key"":{""type"":""Integer"",""value"":""1""},""value"":{""type"":""Integer"",""value"":""2""}}]},{""type"":""Map"",""value"":[{""key"":{""type"":""Integer"",""value"":""1""},""value"":{""type"":""Integer"",""value"":""2""}}]}]}"); + + // check cyclic reference + map[2] = item; + var action = () => item.ToJson(); + action.Should().Throw(); + } } } From 37a7a399b70de90e31363ae7ca11328f9aee40b7 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Sat, 2 Nov 2024 20:19:15 +0800 Subject: [PATCH 22/53] fix some compiler warnings (#3564) --- benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs | 6 +++--- benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs | 5 ++++- benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs b/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs index 78aa0dd24c..46d5be624f 100644 --- a/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs +++ b/benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs @@ -17,9 +17,9 @@ public abstract class OpCodeBase { [Params(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2040)] public int ItemCount { get; set; } = 10; - protected byte[] baseLineScript; - protected byte[] script; - protected byte[] multiScript; + protected byte[] baseLineScript = Array.Empty(); + protected byte[] script = Array.Empty(); + protected byte[] multiScript = Array.Empty(); [GlobalSetup] public void Setup() diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs index 91c660ff48..f2d78fda6c 100644 --- a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs @@ -18,7 +18,7 @@ namespace Neo.VM.Benchmark; public class Benchmarks_Convert { - private Dictionary> testItemsByType; + private Dictionary>? testItemsByType; [GlobalSetup] public void Setup() @@ -30,6 +30,9 @@ public void Setup() [ArgumentsSource(nameof(GetTypeConversionPairs))] public void BenchConvertTo(StackItemType fromType, StackItemType toType) { + if (testItemsByType is null) + throw new InvalidOperationException($"{nameof(testItemsByType)} not initialized"); + foreach (var item in testItemsByType[fromType]) { try diff --git a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs index 1d9c267f5f..64cc3d6988 100644 --- a/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs +++ b/benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs @@ -95,7 +95,7 @@ private static void CreateNestedArray(Array? rootArray, int depth, int elementsP } } - private static void CreateNestedTestArray(TestArray rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter referenceCounter = null) + private static void CreateNestedTestArray(TestArray rootArray, int depth, int elementsPerLevel = 1, IReferenceCounter? referenceCounter = null) { if (depth < 0) { From a856982dd89c7a5393609f7cd206733e70408249 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 4 Nov 2024 03:17:37 -0500 Subject: [PATCH 23/53] Fixed hashcode for StackItem [Remove Murmur32 and add XxHash3] (#3531) * Fixed HashCodes * Added test item * Added simple hasing for `GetHashCode` for `ByteString` and `Buffer` * Refactor * Cache hashcode * remove ToArray * Fix pointer * Cache hashcode script * Use Xxhash3 * Rename to InvalidateHashCode * Change to internal * Remove Murmur32 * Fixed `GetHashCode` --------- Co-authored-by: Jimmy Co-authored-by: Fernando Diaz Toledano Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo.VM/Cryptography/Murmur32.cs | 98 ---------------------- src/Neo.VM/JumpTable/JumpTable.Compound.cs | 2 + src/Neo.VM/JumpTable/JumpTable.Splice.cs | 1 + src/Neo.VM/Neo.VM.csproj | 4 + src/Neo.VM/Script.cs | 11 +++ src/Neo.VM/Types/Buffer.cs | 10 +++ src/Neo.VM/Types/ByteString.cs | 15 ---- src/Neo.VM/Types/Pointer.cs | 2 +- src/Neo.VM/Types/PrimitiveType.cs | 6 -- src/Neo.VM/Types/StackItem.Vertex.cs | 16 +++- src/Neo.VM/Unsafe.cs | 14 ++++ tests/Neo.VM.Tests/UT_StackItem.cs | 13 ++- 12 files changed, 69 insertions(+), 123 deletions(-) delete mode 100644 src/Neo.VM/Cryptography/Murmur32.cs diff --git a/src/Neo.VM/Cryptography/Murmur32.cs b/src/Neo.VM/Cryptography/Murmur32.cs deleted file mode 100644 index cfb6f8ccd1..0000000000 --- a/src/Neo.VM/Cryptography/Murmur32.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// Murmur32.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.Buffers.Binary; -using System.Numerics; -using System.Security.Cryptography; - -namespace Neo.VM.Cryptography -{ - /// - /// Computes the murmur hash for the input data. - /// - sealed class Murmur32 : HashAlgorithm - { - private const uint c1 = 0xcc9e2d51; - private const uint c2 = 0x1b873593; - private const int r1 = 15; - private const int r2 = 13; - private const uint m = 5; - private const uint n = 0xe6546b64; - - private readonly uint seed; - private uint hash; - private int length; - - public override int HashSize => 32; - - /// - /// Initializes a new instance of the class with the specified seed. - /// - /// The seed to be used. - public Murmur32(uint seed) - { - this.seed = seed; - Initialize(); - } - - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - length += cbSize; - int remainder = cbSize & 3; - int alignedLength = ibStart + (cbSize - remainder); - for (int i = ibStart; i < alignedLength; i += 4) - { - uint k = BinaryPrimitives.ReadUInt32LittleEndian(array.AsSpan(i)); - k *= c1; - k = BitOperations.RotateLeft(k, r1); - k *= c2; - hash ^= k; - hash = BitOperations.RotateLeft(hash, r2); - hash = hash * m + n; - } - if (remainder > 0) - { - uint remainingBytes = 0; - switch (remainder) - { - case 3: remainingBytes ^= (uint)array[alignedLength + 2] << 16; goto case 2; - case 2: remainingBytes ^= (uint)array[alignedLength + 1] << 8; goto case 1; - case 1: remainingBytes ^= array[alignedLength]; break; - } - remainingBytes *= c1; - remainingBytes = BitOperations.RotateLeft(remainingBytes, r1); - remainingBytes *= c2; - hash ^= remainingBytes; - } - } - - protected override byte[] HashFinal() - { - hash ^= (uint)length; - hash ^= hash >> 16; - hash *= 0x85ebca6b; - hash ^= hash >> 13; - hash *= 0xc2b2ae35; - hash ^= hash >> 16; - - byte[] buffer = new byte[sizeof(uint)]; - BinaryPrimitives.WriteUInt32LittleEndian(buffer, hash); - return buffer; - } - - public override void Initialize() - { - hash = seed; - length = 0; - } - } -} diff --git a/src/Neo.VM/JumpTable/JumpTable.Compound.cs b/src/Neo.VM/JumpTable/JumpTable.Compound.cs index 2a81b213fe..2447edfa35 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Compound.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Compound.cs @@ -464,6 +464,7 @@ public virtual void SetItem(ExecutionEngine engine, Instruction instruction) if (b < sbyte.MinValue || b > byte.MaxValue) throw new InvalidOperationException($"Overflow in {instruction.OpCode}, {b} is not a byte type."); buffer.InnerBuffer.Span[index] = (byte)b; + buffer.InvalidateHashCode(); break; } default: @@ -489,6 +490,7 @@ public virtual void ReverseItems(ExecutionEngine engine, Instruction instruction break; case Types.Buffer buffer: buffer.InnerBuffer.Span.Reverse(); + buffer.InvalidateHashCode(); break; default: throw new InvalidOperationException($"Invalid type for {instruction.OpCode}: {x.Type}"); diff --git a/src/Neo.VM/JumpTable/JumpTable.Splice.cs b/src/Neo.VM/JumpTable/JumpTable.Splice.cs index eaa979a8b8..82e7f5750c 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Splice.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Splice.cs @@ -61,6 +61,7 @@ public virtual void Memcpy(ExecutionEngine engine, Instruction instruction) throw new InvalidOperationException($"The value {count} is out of range."); // 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(); } /// diff --git a/src/Neo.VM/Neo.VM.csproj b/src/Neo.VM/Neo.VM.csproj index cc6fb4a3a9..eb7f9e7407 100644 --- a/src/Neo.VM/Neo.VM.csproj +++ b/src/Neo.VM/Neo.VM.csproj @@ -14,4 +14,8 @@ + + + + diff --git a/src/Neo.VM/Script.cs b/src/Neo.VM/Script.cs index 95307c94f4..1721b6e104 100644 --- a/src/Neo.VM/Script.cs +++ b/src/Neo.VM/Script.cs @@ -23,6 +23,7 @@ namespace Neo.VM [DebuggerDisplay("Length={Length}")] public class Script { + private int _hashCode = 0; private readonly ReadOnlyMemory _value; private readonly bool strictMode; private readonly Dictionary _instructions = new(); @@ -157,5 +158,15 @@ public Instruction GetInstruction(int ip) public static implicit operator ReadOnlyMemory(Script script) => script._value; public static implicit operator Script(ReadOnlyMemory script) => new(script); public static implicit operator Script(byte[] script) => new(script); + + public override int GetHashCode() + { + if (_hashCode == 0) + { + return _hashCode = HashCode.Combine(Unsafe.HashBytes(_value.Span)); + } + + return _hashCode; + } } } diff --git a/src/Neo.VM/Types/Buffer.cs b/src/Neo.VM/Types/Buffer.cs index 8d170577e6..8ee38a816d 100644 --- a/src/Neo.VM/Types/Buffer.cs +++ b/src/Neo.VM/Types/Buffer.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Numerics; +using System.Runtime.CompilerServices; namespace Neo.VM.Types { @@ -112,5 +113,14 @@ public override string ToString() { return GetSpan().TryGetString(out var str) ? $"(\"{str}\")" : $"(\"Base64: {Convert.ToBase64String(GetSpan())}\")"; } + + /// + /// Invalidate HashCode + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void InvalidateHashCode() + { + _hashCode = 0; + } } } diff --git a/src/Neo.VM/Types/ByteString.cs b/src/Neo.VM/Types/ByteString.cs index 2d2df2d862..8869092b03 100644 --- a/src/Neo.VM/Types/ByteString.cs +++ b/src/Neo.VM/Types/ByteString.cs @@ -9,9 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.VM.Cryptography; using System; -using System.Buffers.Binary; using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; @@ -29,9 +27,6 @@ public class ByteString : PrimitiveType /// public static readonly ByteString Empty = ReadOnlyMemory.Empty; - private static readonly uint s_seed = unchecked((uint)new Random().Next()); - private int _hashCode = 0; - public override ReadOnlyMemory Memory { get; } public override StackItemType Type => StackItemType.ByteString; @@ -88,16 +83,6 @@ public override bool GetBoolean() return Unsafe.NotZero(GetSpan()); } - public override int GetHashCode() - { - if (_hashCode == 0) - { - using Murmur32 murmur = new(s_seed); - _hashCode = BinaryPrimitives.ReadInt32LittleEndian(murmur.ComputeHash(GetSpan().ToArray())); - } - return _hashCode; - } - public override BigInteger GetInteger() { if (Size > Integer.MaxSize) throw new InvalidCastException($"MaxSize exceed: {Size}"); diff --git a/src/Neo.VM/Types/Pointer.cs b/src/Neo.VM/Types/Pointer.cs index 1f729c812f..1fe5d339df 100644 --- a/src/Neo.VM/Types/Pointer.cs +++ b/src/Neo.VM/Types/Pointer.cs @@ -57,7 +57,7 @@ public override bool GetBoolean() public override int GetHashCode() { - return HashCode.Combine(Script, Position); + return HashCode.Combine(Script.GetHashCode(), Position); } public override string ToString() diff --git a/src/Neo.VM/Types/PrimitiveType.cs b/src/Neo.VM/Types/PrimitiveType.cs index 8415b8a45c..49fa0c6ebc 100644 --- a/src/Neo.VM/Types/PrimitiveType.cs +++ b/src/Neo.VM/Types/PrimitiveType.cs @@ -47,12 +47,6 @@ internal sealed override StackItem DeepCopy(Dictionary ref public abstract override bool Equals(StackItem? other); - /// - /// Get the hash code of the VM object, which is used for key comparison in the . - /// - /// The hash code of this VM object. - public abstract override int GetHashCode(); - public sealed override ReadOnlySpan GetSpan() { return Memory.Span; diff --git a/src/Neo.VM/Types/StackItem.Vertex.cs b/src/Neo.VM/Types/StackItem.Vertex.cs index d3e9ed4dbd..c14a08600a 100644 --- a/src/Neo.VM/Types/StackItem.Vertex.cs +++ b/src/Neo.VM/Types/StackItem.Vertex.cs @@ -80,6 +80,11 @@ internal class ObjectReferenceEntry /// internal int LowLink = 0; + /// + /// Stack Item hashcode + /// + protected int _hashCode = 0; + /// /// Indicates whether the item is currently on the stack for Tarjan's algorithm. /// @@ -120,7 +125,14 @@ internal class ObjectReferenceEntry /// Use this method when you need a hash code for a StackItem. /// /// The hash code for the StackItem. - public override int GetHashCode() => - HashCode.Combine(GetSpan().ToArray()); + public override int GetHashCode() + { + if (_hashCode == 0) + { + return _hashCode = HashCode.Combine(Type, Unsafe.HashBytes(GetSpan())); + } + + return _hashCode; + } } } diff --git a/src/Neo.VM/Unsafe.cs b/src/Neo.VM/Unsafe.cs index e3f4366be2..ae46ef8e82 100644 --- a/src/Neo.VM/Unsafe.cs +++ b/src/Neo.VM/Unsafe.cs @@ -10,12 +10,15 @@ // modifications are permitted. using System; +using System.IO.Hashing; using System.Runtime.CompilerServices; namespace Neo.VM { unsafe internal static class Unsafe { + const long HashMagicNumber = 40343; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool NotZero(ReadOnlySpan x) { @@ -38,5 +41,16 @@ public static bool NotZero(ReadOnlySpan x) } return false; } + + /// + /// Get 64-bit hash code for a byte array + /// + /// Span + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong HashBytes(ReadOnlySpan span) + { + return XxHash3.HashToUInt64(span, HashMagicNumber); + } } } diff --git a/tests/Neo.VM.Tests/UT_StackItem.cs b/tests/Neo.VM.Tests/UT_StackItem.cs index 210fa0d433..6982f2463a 100644 --- a/tests/Neo.VM.Tests/UT_StackItem.cs +++ b/tests/Neo.VM.Tests/UT_StackItem.cs @@ -31,8 +31,17 @@ public void TestHashCode() itemA = new VM.Types.Buffer(1); itemB = new VM.Types.Buffer(1); + itemC = new VM.Types.Buffer(2); - Assert.IsTrue(itemA.GetHashCode() != itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + + itemA = new byte[] { 1, 2, 3 }; + itemB = new byte[] { 1, 2, 3 }; + itemC = new byte[] { 5, 6 }; + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); itemA = true; itemB = true; @@ -67,8 +76,10 @@ public void TestHashCode() itemA = new InteropInterface(123); itemB = new InteropInterface(123); + itemC = new InteropInterface(124); Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); var script = new Script(System.Array.Empty()); itemA = new Pointer(script, 123); From 2f4717a5d10759ba5604a007bc4baeac69454451 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 5 Nov 2024 03:15:36 +0100 Subject: [PATCH 24/53] Extend `CalculateNetworkFee` with contract verification (#3385) * Calculate fee * change values * remove using * fix using * Remove comments * Remove map * Anna's feedback * ensure only one result * Update src/Neo/Wallets/Helper.cs * Remove error * Fix compilation --------- Co-authored-by: Christopher Schuchardt Co-authored-by: Jimmy --- src/Neo/Wallets/Helper.cs | 73 ++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/src/Neo/Wallets/Helper.cs b/src/Neo/Wallets/Helper.cs index 9f24e54764..576660d61e 100644 --- a/src/Neo/Wallets/Helper.cs +++ b/src/Neo/Wallets/Helper.cs @@ -18,6 +18,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; +using Neo.VM.Types; using System; using static Neo.SmartContract.Helper; @@ -118,6 +119,7 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, if (witnessScript is null || witnessScript.Length == 0) { + // Contract-based verification var contract = NativeContract.ContractManagement.GetContract(snapshot, hash); if (contract is null) throw new ArgumentException($"The smart contract or address {hash} ({hash.ToAddress(settings.AddressVersion)}) is not found. " + @@ -128,35 +130,74 @@ public static long CalculateNetworkFee(this Transaction tx, DataCache snapshot, if (md.ReturnType != ContractParameterType.Boolean) throw new ArgumentException("The verify method doesn't return boolean value."); if (md.Parameters.Length > 0 && invocationScript is null) - throw new ArgumentException("The verify method requires parameters that need to be passed via the witness' invocation script."); + { + var script = new ScriptBuilder(); + foreach (var par in md.Parameters) + { + switch (par.Type) + { + case ContractParameterType.Any: + case ContractParameterType.Signature: + case ContractParameterType.String: + case ContractParameterType.ByteArray: + script.EmitPush(new byte[64]); + break; + case ContractParameterType.Boolean: + script.EmitPush(true); + break; + case ContractParameterType.Integer: + script.Emit(OpCode.PUSHINT256, new byte[Integer.MaxSize]); + break; + case ContractParameterType.Hash160: + script.EmitPush(new byte[UInt160.Length]); + break; + case ContractParameterType.Hash256: + script.EmitPush(new byte[UInt256.Length]); + break; + case ContractParameterType.PublicKey: + script.EmitPush(new byte[33]); + break; + case ContractParameterType.Array: + script.Emit(OpCode.NEWARRAY0); + break; + } + } + invocationScript = script.ToArray(); + } // Empty verification and non-empty invocation scripts - var invSize = invocationScript?.GetVarSize() ?? Array.Empty().GetVarSize(); - size += Array.Empty().GetVarSize() + invSize; + var invSize = invocationScript?.GetVarSize() ?? System.Array.Empty().GetVarSize(); + size += System.Array.Empty().GetVarSize() + invSize; // Check verify cost using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CloneCache(), settings: settings, gas: maxExecutionCost); engine.LoadContract(contract, md, CallFlags.ReadOnly); if (invocationScript != null) engine.LoadScript(invocationScript, configureState: p => p.CallFlags = CallFlags.None); - if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); - if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); - + if (engine.Execute() == VMState.HALT) + { + // https://github.com/neo-project/neo/issues/2805 + if (engine.ResultStack.Count != 1) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); + _ = engine.ResultStack.Pop().GetBoolean(); // Ensure that the result is boolean + } maxExecutionCost -= engine.FeeConsumed; if (maxExecutionCost <= 0) throw new InvalidOperationException("Insufficient GAS."); networkFee += engine.FeeConsumed; } - else if (IsSignatureContract(witnessScript)) + else { - size += 67 + witnessScript.GetVarSize(); - networkFee += exec_fee_factor * SignatureContractCost(); - } - else if (IsMultiSigContract(witnessScript, out int m, out int n)) - { - int size_inv = 66 * m; - size += UnsafeData.GetVarSize(size_inv) + size_inv + witnessScript.GetVarSize(); - networkFee += exec_fee_factor * MultiSignatureContractCost(m, n); + // Regular signature verification. + if (IsSignatureContract(witnessScript)) + { + size += 67 + witnessScript.GetVarSize(); + networkFee += exec_fee_factor * SignatureContractCost(); + } + else if (IsMultiSigContract(witnessScript, out int m, out int n)) + { + int size_inv = 66 * m; + size += UnsafeData.GetVarSize(size_inv) + size_inv + witnessScript.GetVarSize(); + networkFee += exec_fee_factor * MultiSignatureContractCost(m, n); + } } - // We can support more contract types in the future. } networkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); foreach (TransactionAttribute attr in tx.Attributes) From 08ecb7edb2019c59ae2725cb34fe3b21945164a7 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 5 Nov 2024 16:46:12 +0800 Subject: [PATCH 25/53] support file path to base64 in parse (#3487) * support path input in parse * FilePathToContentBase64 * allow only .nef file * Update src/Neo.CLI/CLI/MainService.Tools.cs Co-authored-by: Shargon --------- Co-authored-by: Jimmy Co-authored-by: Christopher Schuchardt Co-authored-by: Shargon --- src/Neo.CLI/CLI/MainService.Tools.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index c000b48655..c6951f7cba 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -70,6 +70,17 @@ private void OnParseCommand(string value) } } + /// + /// Read .nef file from path and print its content in base64 + /// + [ParseFunction(".nef file path to content base64")] + private string? NefFileToBase64(string path) + { + if (Path.GetExtension(path).ToLower() != ".nef") return null; + if (!File.Exists(path)) return null; + return Convert.ToBase64String(File.ReadAllBytes(path)); + } + /// /// Little-endian to Big-endian /// input: ce616f7f74617e0fc4b805583af2602a238df63f From a021ebe5581d88289751be83e78c14a788f2410c Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 5 Nov 2024 16:57:52 +0800 Subject: [PATCH 26/53] A simpler and better performance impl for `ByteArrayComparer` (#3563) * A better and simpler impl for ByteArrayComparer * add benchmark * optimization when length not equal * update bench * remove redundant --------- Co-authored-by: Jimmy Co-authored-by: Shargon --- .../Benchmark.ByteArrayComparer.cs | 165 ++++++++++++++++++ .../Neo.Extensions.Benchmarks.csproj | 17 ++ .../OldByteArrayComparer.cs | 18 +- .../Neo.Extensions.Benchmarks/Program.cs | 15 ++ neo.sln | 7 + src/Neo.Extensions/ByteArrayComparer.cs | 46 +++++ src/Neo/Persistence/DataCache.cs | 2 +- src/Neo/Persistence/MemorySnapshot.cs | 1 + src/Neo/Persistence/MemoryStore.cs | 1 + .../UT_ByteArrayComparer.cs | 40 ++++- 10 files changed, 293 insertions(+), 19 deletions(-) create mode 100644 benchmarks/Neo.Extensions.Benchmarks/Benchmark.ByteArrayComparer.cs create mode 100644 benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj rename src/Neo.IO/ByteArrayComparer.cs => benchmarks/Neo.Extensions.Benchmarks/OldByteArrayComparer.cs (73%) create mode 100644 benchmarks/Neo.Extensions.Benchmarks/Program.cs create mode 100644 src/Neo.Extensions/ByteArrayComparer.cs rename tests/{Neo.UnitTests/IO => Neo.Extensions.Tests}/UT_ByteArrayComparer.cs (52%) diff --git a/benchmarks/Neo.Extensions.Benchmarks/Benchmark.ByteArrayComparer.cs b/benchmarks/Neo.Extensions.Benchmarks/Benchmark.ByteArrayComparer.cs new file mode 100644 index 0000000000..b41d1b54c4 --- /dev/null +++ b/benchmarks/Neo.Extensions.Benchmarks/Benchmark.ByteArrayComparer.cs @@ -0,0 +1,165 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Benchmark.ByteArrayComparer.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; + +namespace Neo.Extensions +{ + public class Benchmark_ByteArrayComparer + { + private ByteArrayComparer comparer = ByteArrayComparer.Default; + private ByteArrayComparerV0 _oldComparer = ByteArrayComparerV0.Default; + private byte[]? x, y; + + [GlobalSetup] + public void Setup() + { + comparer = ByteArrayComparer.Default; + _oldComparer = ByteArrayComparerV0.Default; + } + + [GlobalSetup(Target = nameof(NewCompare_50Bytes))] + public void SetupNew50Bytes() + { + comparer = ByteArrayComparer.Default; + + x = new byte[50]; // 50 bytes + y = new byte[50]; // 50 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void NewCompare_50Bytes() + { + comparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(OldCompare_50Bytes))] + public void SetupOld50Bytes() + { + _oldComparer = ByteArrayComparerV0.Default; + + x = new byte[50]; // 50 bytes + y = new byte[50]; // 50 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void OldCompare_50Bytes() + { + _oldComparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(NewCompare_500Bytes))] + public void SetupNew500Bytes() + { + comparer = ByteArrayComparer.Default; + + x = new byte[500]; // 500 bytes + y = new byte[500]; // 500 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void NewCompare_500Bytes() + { + comparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(OldCompare_500Bytes))] + public void SetupOld500Bytes() + { + _oldComparer = ByteArrayComparerV0.Default; + + x = new byte[500]; // 500 bytes + y = new byte[500]; // 500 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void OldCompare_500Bytes() + { + _oldComparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(NewCompare_5000Bytes))] + public void SetupNew5000Bytes() + { + comparer = ByteArrayComparer.Default; + + x = new byte[5000]; // 5000 bytes + y = new byte[5000]; // 5000 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void NewCompare_5000Bytes() + { + comparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(OldCompare_5000Bytes))] + public void SetupOld5000Bytes() + { + _oldComparer = ByteArrayComparerV0.Default; + + x = new byte[5000]; // 5000 bytes + y = new byte[5000]; // 5000 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void OldCompare_5000Bytes() + { + _oldComparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(NewCompare_50000Bytes))] + public void SetupNew50000Bytes() + { + comparer = ByteArrayComparer.Default; + + x = new byte[50000]; // 50000 bytes + y = new byte[50000]; // 50000 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void NewCompare_50000Bytes() + { + comparer.Compare(x, y); + } + + [GlobalSetup(Target = nameof(OldCompare_50000Bytes))] + public void SetupOld50000Bytes() + { + _oldComparer = ByteArrayComparerV0.Default; + + x = new byte[50000]; // 50000 bytes + y = new byte[50000]; // 50000 bytes + Array.Fill(x, (byte)0xCC); + Array.Copy(x, y, x.Length); + } + + [Benchmark] + public void OldCompare_50000Bytes() + { + _oldComparer.Compare(x, y); + } + } +} diff --git a/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj b/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj new file mode 100644 index 0000000000..d87bccaee5 --- /dev/null +++ b/benchmarks/Neo.Extensions.Benchmarks/Neo.Extensions.Benchmarks.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + Neo.Extensions + enable + enable + + + + + + + + + diff --git a/src/Neo.IO/ByteArrayComparer.cs b/benchmarks/Neo.Extensions.Benchmarks/OldByteArrayComparer.cs similarity index 73% rename from src/Neo.IO/ByteArrayComparer.cs rename to benchmarks/Neo.Extensions.Benchmarks/OldByteArrayComparer.cs index f9d44e24d6..6c9716403c 100644 --- a/src/Neo.IO/ByteArrayComparer.cs +++ b/benchmarks/Neo.Extensions.Benchmarks/OldByteArrayComparer.cs @@ -1,6 +1,6 @@ // Copyright (C) 2015-2024 The Neo Project. // -// ByteArrayComparer.cs file belongs to the neo project and is free +// OldByteArrayComparer.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 @@ -9,20 +9,18 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Neo.IO +namespace Neo.Extensions { - internal class ByteArrayComparer : IComparer + public class ByteArrayComparerV0 : IComparer { - public static readonly ByteArrayComparer Default = new(1); - public static readonly ByteArrayComparer Reverse = new(-1); + public static readonly ByteArrayComparerV0 Default = new(1); + public static readonly ByteArrayComparerV0 Reverse = new(-1); private readonly int _direction; - private ByteArrayComparer(int direction) + internal ByteArrayComparerV0(int direction) { _direction = direction; } @@ -35,8 +33,8 @@ public int Compare(byte[]? x, byte[]? y) if (y is null && x is not null) return _direction > 0 ? x.Length : -x.Length; return _direction > 0 ? - CompareInternal(x!, y!) : - -CompareInternal(x!, y!); + CompareInternal(x!, y!) : + -CompareInternal(x!, y!); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/benchmarks/Neo.Extensions.Benchmarks/Program.cs b/benchmarks/Neo.Extensions.Benchmarks/Program.cs new file mode 100644 index 0000000000..c3c9be7cc7 --- /dev/null +++ b/benchmarks/Neo.Extensions.Benchmarks/Program.cs @@ -0,0 +1,15 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Program.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.Running; +using Neo.Extensions; + +BenchmarkRunner.Run(typeof(Benchmark_ByteArrayComparer)); diff --git a/neo.sln b/neo.sln index 5e7f8c7dda..e746e07e7f 100644 --- a/neo.sln +++ b/neo.sln @@ -82,6 +82,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neo.Plugins.ApplicationLogs EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Extensions.Tests", "tests\Neo.Extensions.Tests\Neo.Extensions.Tests.csproj", "{77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neo.Extensions.Benchmarks", "benchmarks\Neo.Extensions.Benchmarks\Neo.Extensions.Benchmarks.csproj", "{B6CB2559-10F9-41AC-8D58-364BFEF9688B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -228,6 +230,10 @@ Global {77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F}.Debug|Any CPU.Build.0 = Debug|Any CPU {77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F}.Release|Any CPU.ActiveCfg = Release|Any CPU {77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F}.Release|Any CPU.Build.0 = Release|Any CPU + {B6CB2559-10F9-41AC-8D58-364BFEF9688B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6CB2559-10F9-41AC-8D58-364BFEF9688B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6CB2559-10F9-41AC-8D58-364BFEF9688B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6CB2559-10F9-41AC-8D58-364BFEF9688B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -269,6 +275,7 @@ Global {185ADAFC-BFC6-413D-BC2E-97F9FB0A8AF0} = {C2DC830A-327A-42A7-807D-295216D30DBB} {8C866DC8-2E55-4399-9563-2F47FD4602EC} = {7F257712-D033-47FF-B439-9D4320D06599} {77FDEE2E-9381-4BFC-B9E6-741EDBD6B90F} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + {B6CB2559-10F9-41AC-8D58-364BFEF9688B} = {C25EB0B0-0CAC-4CC1-8F36-F9229EFB99EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BCBA19D9-F868-4C6D-8061-A2B91E06E3EC} diff --git a/src/Neo.Extensions/ByteArrayComparer.cs b/src/Neo.Extensions/ByteArrayComparer.cs new file mode 100644 index 0000000000..661b76d903 --- /dev/null +++ b/src/Neo.Extensions/ByteArrayComparer.cs @@ -0,0 +1,46 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ByteArrayComparer.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; +using System.Runtime.CompilerServices; + +namespace Neo.Extensions +{ + public class ByteArrayComparer : IComparer + { + public static readonly ByteArrayComparer Default = new(1); + public static readonly ByteArrayComparer Reverse = new(-1); + + private readonly int _direction; + + private ByteArrayComparer(int direction) + { + _direction = direction; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Compare(byte[]? x, byte[]? y) + { + if (x == y) return 0; + + if (x is null) // y must not be null + return -y!.Length * _direction; + + if (y is null) // x must not be null + return x.Length * _direction; + + if (_direction < 0) + return y.AsSpan().SequenceCompareTo(x.AsSpan()); + return x.AsSpan().SequenceCompareTo(y.AsSpan()); + } + } +} diff --git a/src/Neo/Persistence/DataCache.cs b/src/Neo/Persistence/DataCache.cs index 29c610c6c8..37664bfa67 100644 --- a/src/Neo/Persistence/DataCache.cs +++ b/src/Neo/Persistence/DataCache.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/MemorySnapshot.cs b/src/Neo/Persistence/MemorySnapshot.cs index b3d06798d4..4817cc963e 100644 --- a/src/Neo/Persistence/MemorySnapshot.cs +++ b/src/Neo/Persistence/MemorySnapshot.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.Concurrent; using System.Collections.Generic; diff --git a/src/Neo/Persistence/MemoryStore.cs b/src/Neo/Persistence/MemoryStore.cs index 378d5374f1..3107ebdddd 100644 --- a/src/Neo/Persistence/MemoryStore.cs +++ b/src/Neo/Persistence/MemoryStore.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.Concurrent; using System.Collections.Generic; diff --git a/tests/Neo.UnitTests/IO/UT_ByteArrayComparer.cs b/tests/Neo.Extensions.Tests/UT_ByteArrayComparer.cs similarity index 52% rename from tests/Neo.UnitTests/IO/UT_ByteArrayComparer.cs rename to tests/Neo.Extensions.Tests/UT_ByteArrayComparer.cs index 19c3fafcd6..27dbe3f52c 100644 --- a/tests/Neo.UnitTests/IO/UT_ByteArrayComparer.cs +++ b/tests/Neo.Extensions.Tests/UT_ByteArrayComparer.cs @@ -11,10 +11,9 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; using System; -namespace Neo.UnitTests.IO +namespace Neo.Extensions.Tests { [TestClass] public class UT_ByteArrayComparer @@ -32,31 +31,56 @@ public void TestCompare() comparer.Compare(x, x).Should().Be(0); y = null; - comparer.Compare(x, y).Should().Be(5); + comparer.Compare(x, y).Should().BeGreaterThan(0); y = x; x = null; - comparer.Compare(x, y).Should().Be(-5); + comparer.Compare(x, y).Should().BeLessThan(0); x = new byte[] { 1 }; y = Array.Empty(); - comparer.Compare(x, y).Should().Be(1); + comparer.Compare(x, y).Should().BeGreaterThan(0); y = x; comparer.Compare(x, y).Should().Be(0); x = new byte[] { 1 }; y = new byte[] { 2 }; - comparer.Compare(x, y).Should().Be(-1); + comparer.Compare(x, y).Should().BeLessThan(0); + comparer.Compare(null, Array.Empty()).Should().Be(0); + comparer.Compare(Array.Empty(), null).Should().Be(0); + + x = new byte[] { 1, 2, 3, 4, 5 }; + y = new byte[] { 1, 2, 3 }; + comparer.Compare(x, y).Should().BeGreaterThan(0); + + x = new byte[] { 1, 2, 3, 4, 5 }; + y = new byte[] { 1, 2, 3, 4, 5, 6 }; + comparer.Compare(x, y).Should().BeLessThan(0); + + // cases for reverse comparer comparer = ByteArrayComparer.Reverse; + x = new byte[] { 3 }; - comparer.Compare(x, y).Should().Be(-1); + comparer.Compare(x, y).Should().BeLessThan(0); + y = x; comparer.Compare(x, y).Should().Be(0); x = new byte[] { 1 }; y = new byte[] { 2 }; - comparer.Compare(x, y).Should().Be(1); + comparer.Compare(x, y).Should().BeGreaterThan(0); + + comparer.Compare(null, Array.Empty()).Should().Be(0); + comparer.Compare(Array.Empty(), null).Should().Be(0); + + x = new byte[] { 1, 2, 3, 4, 5 }; + y = new byte[] { 1, 2, 3 }; + comparer.Compare(x, y).Should().BeLessThan(0); + + x = new byte[] { 1, 2, 3, 4, 5 }; + y = new byte[] { 1, 2, 3, 4, 5, 6 }; + comparer.Compare(x, y).Should().BeGreaterThan(0); } } } From 25554dfe2532a19b15be95258247a028eba3eabb Mon Sep 17 00:00:00 2001 From: nan01ab Date: Wed, 6 Nov 2024 10:48:18 +0800 Subject: [PATCH 27/53] use `Xxhash3` for StorageKey.GetHashCode (#3559) * fea: StorageKey.GetHashCode uses XxHash3 for better performance * fea: StorageKey.GetHashCode uses XxHash3 for better performance * fea: StorageKey.GetHashCode uses XxHash3 for better performance * use HashCode.Combine * Unify seed * fix ut * remove unused --------- Co-authored-by: Shargon --- .gitignore | 3 + benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs | 56 +++++++++++++++++++ .../Neo.Benchmarks/Neo.Benchmarks.csproj | 1 + benchmarks/Neo.Benchmarks/Program.cs | 1 + src/Neo.VM/Unsafe.cs | 9 +-- src/Neo/Cryptography/Helper.cs | 27 +++++++++ src/Neo/Neo.csproj | 1 + src/Neo/SmartContract/StorageKey.cs | 11 +++- .../Cryptography/UT_Cryptography_Helper.cs | 15 +++-- tests/Neo.UnitTests/Ledger/UT_StorageKey.cs | 7 ++- 10 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs diff --git a/.gitignore b/.gitignore index 1358af3bde..45ccb8c40a 100644 --- a/.gitignore +++ b/.gitignore @@ -257,3 +257,6 @@ paket-files/ PublishProfiles /.vscode launchSettings.json + +# Benchmarks +**/BenchmarkDotNet.Artifacts/ \ No newline at end of file diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs b/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs new file mode 100644 index 0000000000..a63542bace --- /dev/null +++ b/benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Benchmarks.Hash.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 Neo.Cryptography; +using Neo.Extensions; +using System.Diagnostics; +using System.IO.Hashing; +using System.Text; + +namespace Neo.Benchmark; + +public class Benchmarks_Hash +{ + // 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/Neo.Benchmarks.csproj b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj index a59fc6e728..6a4bd93264 100644 --- a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj +++ b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj @@ -9,6 +9,7 @@ + diff --git a/benchmarks/Neo.Benchmarks/Program.cs b/benchmarks/Neo.Benchmarks/Program.cs index c44b76f839..a64a1ca981 100644 --- a/benchmarks/Neo.Benchmarks/Program.cs +++ b/benchmarks/Neo.Benchmarks/Program.cs @@ -14,3 +14,4 @@ // BenchmarkRunner.Run(); BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/src/Neo.VM/Unsafe.cs b/src/Neo.VM/Unsafe.cs index ae46ef8e82..86380722b6 100644 --- a/src/Neo.VM/Unsafe.cs +++ b/src/Neo.VM/Unsafe.cs @@ -17,7 +17,7 @@ namespace Neo.VM { unsafe internal static class Unsafe { - const long HashMagicNumber = 40343; + private const long DefaultXxHash3Seed = 40343; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool NotZero(ReadOnlySpan x) @@ -46,11 +46,12 @@ public static bool NotZero(ReadOnlySpan x) /// Get 64-bit hash code for a byte array /// /// Span - /// + /// The seed used by the xxhash3 algorithm. + /// The computed hash code. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong HashBytes(ReadOnlySpan span) + public static ulong HashBytes(ReadOnlySpan span, long seed = DefaultXxHash3Seed) { - return XxHash3.HashToUInt64(span, HashMagicNumber); + return XxHash3.HashToUInt64(span, seed); } } } diff --git a/src/Neo/Cryptography/Helper.cs b/src/Neo/Cryptography/Helper.cs index 41e89f5a49..761073463b 100644 --- a/src/Neo/Cryptography/Helper.cs +++ b/src/Neo/Cryptography/Helper.cs @@ -18,6 +18,7 @@ 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; @@ -31,7 +32,9 @@ namespace Neo.Cryptography /// public static class Helper { + private const int DefaultXxHash3Seed = 40343; private static readonly bool IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + /// /// Computes the hash value for the specified byte array using the ripemd160 algorithm. /// @@ -117,6 +120,30 @@ 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/Neo.csproj b/src/Neo/Neo.csproj index 7c6478c33e..89c200e31b 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Neo/SmartContract/StorageKey.cs b/src/Neo/SmartContract/StorageKey.cs index 9c5e37827f..7a643a2737 100644 --- a/src/Neo/SmartContract/StorageKey.cs +++ b/src/Neo/SmartContract/StorageKey.cs @@ -33,8 +33,15 @@ public sealed record StorageKey private byte[] cache = null; + // NOTE: StorageKey is readonly, so we can cache the hash code. + private int _hashCode = 0; + public StorageKey() { } + /// + /// Initializes a new instance of the class. + /// + /// The cached byte array. NOTE: It must be read-only and can be modified by the caller. internal StorageKey(byte[] cache) { this.cache = cache; @@ -67,7 +74,9 @@ public bool Equals(StorageKey other) public override int GetHashCode() { - return Id + (int)Key.Span.Murmur32(0); + if (_hashCode == 0) + _hashCode = HashCode.Combine(Id, Key.Span.XxHash3_32()); + return _hashCode; } public byte[] ToArray() diff --git a/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 8cddc26d16..194d65c35b 100644 --- a/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/Neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -19,6 +19,8 @@ using Neo.Wallets; using Neo.Wallets.NEP6; using System; +using System.Collections.Generic; +using System.IO.Hashing; using System.Linq; using System.Text; @@ -55,13 +57,19 @@ 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() { byte[] value = Encoding.ASCII.GetBytes("hello world"); byte[] result = value.Sha256(0, value.Length); - string resultStr = result.ToHexString(); - resultStr.Should().Be("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"); + result.ToHexString().Should().Be("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"); value.Sha256().Should().Equal(result); ((Span)value).Sha256().Should().Equal(result); ((ReadOnlySpan)value).Sha256().Should().Equal(result); @@ -82,8 +90,7 @@ public void TestRIPEMD160() { ReadOnlySpan value = Encoding.ASCII.GetBytes("hello world"); byte[] result = value.RIPEMD160(); - string resultStr = result.ToHexString(); - resultStr.Should().Be("98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f"); + result.ToHexString().Should().Be("98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f"); } [TestMethod] diff --git a/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs b/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs index f2d9300fee..a3bcefb37c 100644 --- a/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/tests/Neo.UnitTests/Ledger/UT_StorageKey.cs @@ -11,7 +11,9 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; using Neo.SmartContract; +using System; namespace Neo.UnitTests.Ledger { @@ -102,8 +104,9 @@ public void Equals_SameHash_DiffKey() [TestMethod] public void GetHashCode_Get() { - StorageKey uut = new() { Id = 0x42000000, Key = TestUtils.GetByteArray(10, 0x42) }; - uut.GetHashCode().Should().Be(1374529787); + var data = TestUtils.GetByteArray(10, 0x42); + StorageKey uut = new() { Id = 0x42000000, Key = data }; + uut.GetHashCode().Should().Be(HashCode.Combine(0x42000000, data.XxHash3_32())); } [TestMethod] From 42f0efdd090ba925251dc5098044aa42490a613e Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 6 Nov 2024 02:35:32 -0500 Subject: [PATCH 28/53] [`Fix`] StackItem.GetHashCode for CompoundType (#3549) * Fixed #3544 * Fixed SubItems of CompoundType * Reused `Unsafe.HashBytes` for hashing function in compoundtype * Fixed test * simple hashcode for compoundtype * Test circular reference * Fixed compoundType * Add one more test case * Fixed test * changed to use subclass * add comment * Add more to GetHashCode for CompoundType * Clean code --------- Co-authored-by: Jimmy Co-authored-by: Fernando Diaz Toledano Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo.VM/Types/CompoundType.cs | 26 +++++++++++++++-- tests/Neo.VM.Tests/UT_StackItem.cs | 45 ++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/Neo.VM/Types/CompoundType.cs b/src/Neo.VM/Types/CompoundType.cs index 6b0da819ec..33f670bfcb 100644 --- a/src/Neo.VM/Types/CompoundType.cs +++ b/src/Neo.VM/Types/CompoundType.cs @@ -60,12 +60,32 @@ public sealed override bool GetBoolean() } /// - /// The operation is not supported. Always throw . + /// + /// This method provides a hash code for the based on its item's span. + /// It is used for efficient storage and retrieval in hash-based collections. + /// + /// Use this method when you need a hash code for a . /// - /// This method always throws the exception. + /// The hash code for the . public override int GetHashCode() { - throw new NotSupportedException(); + var h = new HashCode(); + h.Add(Count); + h.Add(Type); + foreach (var item in SubItems) + { + // This isn't prefect and leaves somethings unsolved. + if (item is CompoundType cItem) + { + h.Add(cItem.Count); + h.Add(cItem.Type); + } + else + { + h.Add(item.GetHashCode()); + } + } + return h.ToHashCode(); } public override string ToString() diff --git a/tests/Neo.VM.Tests/UT_StackItem.cs b/tests/Neo.VM.Tests/UT_StackItem.cs index 6982f2463a..61f6de74a5 100644 --- a/tests/Neo.VM.Tests/UT_StackItem.cs +++ b/tests/Neo.VM.Tests/UT_StackItem.cs @@ -19,6 +19,21 @@ namespace Neo.Test [TestClass] public class UT_StackItem { + [TestMethod] + public void TestCircularReference() + { + var itemA = new Struct { true, false }; + var itemB = new Struct { true, false }; + var itemC = new Struct { false, false }; + + itemA[1] = itemA; + itemB[1] = itemB; + itemC[1] = itemC; + + Assert.AreEqual(itemA.GetHashCode(), itemB.GetHashCode()); + Assert.AreNotEqual(itemA.GetHashCode(), itemC.GetHashCode()); + } + [TestMethod] public void TestHashCode() { @@ -62,17 +77,35 @@ public void TestHashCode() Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); - itemA = new VM.Types.Array(); + itemA = new Array { true, false, 0 }; + itemB = new Array { true, false, 0 }; + itemC = new Array { true, false, 1 }; + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + + itemA = new Struct { true, false, 0 }; + itemB = new Struct { true, false, 0 }; + itemC = new Struct { true, false, 1 }; - Assert.ThrowsException(() => itemA.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); - itemA = new Struct(); + itemA = new Map { [true] = false, [0] = 1 }; + itemB = new Map { [true] = false, [0] = 1 }; + itemC = new Map { [true] = false, [0] = 2 }; - Assert.ThrowsException(() => itemA.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); - itemA = new Map(); + // Test CompoundType GetHashCode for subitems + var junk = new Array { true, false, 0 }; + itemA = new Map { [true] = junk, [0] = junk }; + itemB = new Map { [true] = junk, [0] = junk }; + itemC = new Map { [true] = junk, [0] = 2 }; - Assert.ThrowsException(() => itemA.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); itemA = new InteropInterface(123); itemB = new InteropInterface(123); From 64e13bbe85863328f5d3cc7e7cf479f4ad3d5997 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Thu, 7 Nov 2024 19:16:00 +0800 Subject: [PATCH 29/53] `[Fix]` unalignment memory load in Neo.VM Unsafe.NotZero(ReadOnlySpan) and remove the use of `unsafe` (#3492) * fix: unalignment load in ReadOnlySpan.NotZero * Update .gitignore * use SequenceEqual to compare bytes for better performace * use ContainsAnyExcept to get better performace and simply code * use ContainsAnyExcept to get better performance and simply code * use ContainsAnyExcept to get better performance and simply code * fix comment * merge master and avoid unnecessary change * move notzero test * change to block namespace * Add attribute --------- Co-authored-by: Shargon Co-authored-by: Christopher Schuchardt Co-authored-by: Jimmy --- .gitignore | 4 +++- src/Neo.VM/Types/ByteString.cs | 2 +- src/Neo.VM/Unsafe.cs | 11 ++++++++++- tests/Neo.VM.Tests/UT_Unsafe.cs | 33 ++++++++++++++++++++++++++------- 4 files changed, 40 insertions(+), 10 deletions(-) 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/src/Neo.VM/Types/ByteString.cs b/src/Neo.VM/Types/ByteString.cs index 8869092b03..d2f1ebee65 100644 --- a/src/Neo.VM/Types/ByteString.cs +++ b/src/Neo.VM/Types/ByteString.cs @@ -80,7 +80,7 @@ internal bool Equals(StackItem? other, ref uint limits) public override bool GetBoolean() { if (Size > Integer.MaxSize) throw new InvalidCastException(); - return Unsafe.NotZero(GetSpan()); + return GetSpan().NotZero(); } public override BigInteger GetInteger() 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/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; + } + } } } } From 6ad3d1696e9c51836ce6bae2838a6bba71b92195 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 7 Nov 2024 21:54:42 -0500 Subject: [PATCH 30/53] [Fix] Enable Storage tests for (MacOS) (#3568) * Fixed Tests * fixed Paths * Update for all branches * Changed OS to MACOS * Fixed environment path * Fixed Copy of the leveldb file * Fixed load order * Fixed file paths for libleveldb * sfas * dsg * fixed * Uncommented code --------- Co-authored-by: Shargon --- .github/workflows/main.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 22cf4d08df..e70c6e4cd3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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/net8.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 From 8b2c33bf211b743ec275053be763a54982010192 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Fri, 8 Nov 2024 15:56:05 +0800 Subject: [PATCH 31/53] Optimize the ` ToHexString` (#3566) * fea: optimize ToHexString * use char array instead string * 1. const string is faster; 2. Scalar.ToString uses ToHexString * Use StringBuilder * Optimize capacity * Reduce changes --------- Co-authored-by: Fernando Diaz Toledano Co-authored-by: Jimmy Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- .../Neo.Cryptography.BLS12_381.csproj | 4 ++ src/Neo.Cryptography.BLS12_381/Scalar.cs | 9 ++-- src/Neo.Extensions/ByteExtensions.cs | 54 +++++++++++++++---- 3 files changed, 52 insertions(+), 15 deletions(-) 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..cbed7d23b8 100644 --- a/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj +++ b/src/Neo.Cryptography.BLS12_381/Neo.Cryptography.BLS12_381.csproj @@ -13,4 +13,8 @@ + + + + diff --git a/src/Neo.Cryptography.BLS12_381/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index ef40e9b817..a4e93efe55 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; @@ -119,11 +120,11 @@ private Span GetSpanU64() public override string ToString() { - byte[] bytes = ToArray(); - StringBuilder sb = new(); + var bytes = ToArray(); + + StringBuilder sb = new(2 + (bytes.Length * 2)); sb.Append("0x"); - for (int i = bytes.Length - 1; i >= 0; i--) - sb.AppendFormat("{0:x2}", bytes[i]); + sb.Append(bytes.ToHexString(true)); return sb.ToString(); } diff --git a/src/Neo.Extensions/ByteExtensions.cs b/src/Neo.Extensions/ByteExtensions.cs index 013a8ef1cc..73aed3e388 100644 --- a/src/Neo.Extensions/ByteExtensions.cs +++ b/src/Neo.Extensions/ByteExtensions.cs @@ -10,23 +10,36 @@ // modifications are permitted. using System; +using System.Runtime.CompilerServices; using System.Text; namespace Neo.Extensions { public static class ByteExtensions { + private const string s_hexChars = "0123456789abcdef"; + /// /// Converts a byte array to hex . /// /// The byte array to convert. /// The converted hex . + [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 + 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 +48,21 @@ 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 . + [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); + + 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 +70,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 } } } From 406414b6ff135444c99634158daa306609b66e53 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 8 Nov 2024 07:14:44 -0500 Subject: [PATCH 32/53] [`Fix`] namespace scopes (#3503) * Changed namespace scope from file-scoped to block-scoped. * dotnet format * merge master and fix conflict * format * Update benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_DeepCopy.cs * Update benchmarks/Neo.VM.Benchmarks/VMTypes/Benchmarks_Convert.cs * Update Benchmarks_Convert.cs * Update benchmarks/Neo.VM.Benchmarks/OpCode/OpCodeBase.cs --------- Co-authored-by: Jimmy Co-authored-by: Shargon Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- .editorconfig | 5 + benchmarks/Neo.Benchmarks/Benchmarks.Hash.cs | 69 +- benchmarks/Neo.Benchmarks/Benchmarks.POC.cs | 111 +- .../Neo.Benchmarks/Benchmarks.UInt160.cs | 281 ++-- .../InstructionBuilder/Helper.cs | 85 +- .../InstructionBuilder/Instruction.cs | 65 +- .../InstructionBuilder/InstructionBuilder.cs | 397 ++--- .../InstructionBuilder/JumpTarget.cs | 9 +- .../OpCode/Arrays/OpCode.ReverseN.cs | 57 +- .../OpCode/Benchmark.Opcode.cs | 433 ++--- .../OpCode/BenchmarkEngine.cs | 239 +-- .../Neo.VM.Benchmarks/OpCode/BenchmarkMode.cs | 13 +- .../Neo.VM.Benchmarks/OpCode/OpCodeBase.cs | 57 +- benchmarks/Neo.VM.Benchmarks/TestArray.cs | 219 +-- benchmarks/Neo.VM.Benchmarks/TestStruct.cs | 185 +-- .../VMTypes/Benchmarks_Convert.cs | 135 +- .../VMTypes/Benchmarks_DeepCopy.cs | 155 +- src/Neo.Cryptography.BLS12_381/Bls12.Adder.cs | 83 +- src/Neo.Cryptography.BLS12_381/Bls12.cs | 23 +- .../ConstantTimeUtility.cs | 31 +- src/Neo.Cryptography.BLS12_381/Constants.cs | 11 +- src/Neo.Cryptography.BLS12_381/Fp.cs | 839 +++++----- src/Neo.Cryptography.BLS12_381/Fp12.cs | 349 ++-- src/Neo.Cryptography.BLS12_381/Fp2.cs | 457 +++--- src/Neo.Cryptography.BLS12_381/Fp6.cs | 509 +++--- src/Neo.Cryptography.BLS12_381/FpConstants.cs | 129 +- src/Neo.Cryptography.BLS12_381/G1Affine.cs | 261 +-- src/Neo.Cryptography.BLS12_381/G1Constants.cs | 77 +- .../G1Projective.cs | 465 +++--- src/Neo.Cryptography.BLS12_381/G2Affine.cs | 339 ++-- src/Neo.Cryptography.BLS12_381/G2Constants.cs | 185 +-- .../G2Prepared.Adder.cs | 87 +- src/Neo.Cryptography.BLS12_381/G2Prepared.cs | 27 +- .../G2Projective.cs | 609 +++---- src/Neo.Cryptography.BLS12_381/Gt.cs | 185 +-- src/Neo.Cryptography.BLS12_381/GtConstants.cs | 203 +-- .../IMillerLoopDriver.cs | 17 +- src/Neo.Cryptography.BLS12_381/INumber.cs | 79 +- src/Neo.Cryptography.BLS12_381/MathUtility.cs | 91 +- .../MillerLoopResult.cs | 211 +-- .../MillerLoopUtility.cs | 181 +-- src/Neo.Cryptography.BLS12_381/Scalar.cs | 867 +++++----- .../ScalarConstants.cs | 145 +- src/Neo.IO/Caching/KeyedCollectionSlim.cs | 59 +- src/Neo.Json/JContainer.cs | 23 +- src/Neo.Json/JPathTokenType.cs | 27 +- src/Neo.Json/JToken.cs | 485 +++--- .../OrderedDictionary.KeyCollection.cs | 47 +- .../OrderedDictionary.ValueCollection.cs | 47 +- src/Neo.Json/Utility.cs | 19 +- src/Neo/IEventHandlers/ICommittedHandler.cs | 21 +- src/Neo/IEventHandlers/ICommittingHandler.cs | 25 +- src/Neo/IEventHandlers/ILogHandler.cs | 21 +- src/Neo/IEventHandlers/ILoggingHandler.cs | 23 +- .../IEventHandlers/IMessageReceivedHandler.cs | 21 +- src/Neo/IEventHandlers/INotifyHandler.cs | 21 +- .../IEventHandlers/IServiceAddedHandler.cs | 21 +- .../ITransactionAddedHandler.cs | 21 +- .../ITransactionRemovedHandler.cs | 21 +- .../IEventHandlers/IWalletChangedHandler.cs | 21 +- src/Neo/Ledger/TransactionRemovedEventArgs.cs | 25 +- src/Neo/Persistence/StoreFactory.cs | 71 +- src/Neo/Plugins/PluginSettings.cs | 21 +- .../Native/CryptoLib.BLS12_381.cs | 231 +-- .../RpcServer/Model/BlockHashOrIndex.cs | 73 +- .../RpcServer/Model/ContractNameOrHashOrId.cs | 105 +- src/Plugins/RpcServer/ParameterConverter.cs | 193 +-- .../RpcServer/RpcMethodWithParamsAttribute.cs | 11 +- src/Plugins/SQLiteWallet/Account.cs | 11 +- src/Plugins/SQLiteWallet/Address.cs | 9 +- src/Plugins/SQLiteWallet/Contract.cs | 17 +- src/Plugins/SQLiteWallet/Key.cs | 11 +- src/Plugins/SQLiteWallet/SQLiteWallet.cs | 623 +++---- .../SQLiteWallet/SQLiteWalletAccount.cs | 25 +- .../SQLiteWallet/SQLiteWalletFactory.cs | 41 +- .../SQLiteWallet/VerificationContract.cs | 65 +- src/Plugins/SQLiteWallet/WalletDataContext.cs | 85 +- .../Neo.Cryptography.BLS12_381.Tests/UT_Fp.cs | 613 +++---- .../UT_Fp12.cs | 651 ++++---- .../UT_Fp2.cs | 881 +++++----- .../UT_Fp6.cs | 319 ++-- .../Neo.Cryptography.BLS12_381.Tests/UT_G1.cs | 989 ++++++------ .../Neo.Cryptography.BLS12_381.Tests/UT_G2.cs | 1431 +++++++++-------- .../UT_Pairings.cs | 93 +- .../UT_Scalar.cs | 631 ++++---- .../TestMemoryStoreProvider.cs | 13 +- .../UT_Parameters.cs | 785 ++++----- .../Neo.Plugins.RpcServer.Tests/UT_Result.cs | 17 +- .../UT_RpcServer.SmartContract.cs | 391 ++--- .../UT_RpcServer.Utilities.cs | 49 +- .../UT_RpcServer.Wallet.cs | 857 +++++----- .../Persistence/TestMemoryStoreProvider.cs | 13 +- .../Persistence/UT_MemoryClonedCache.cs | 217 +-- .../Persistence/UT_MemorySnapshot.cs | 221 +-- .../Persistence/UT_MemorySnapshotCache.cs | 229 +-- tests/Neo.UnitTests/TestUtils.Block.cs | 289 ++-- tests/Neo.UnitTests/TestUtils.Contract.cs | 155 +- tests/Neo.UnitTests/TestUtils.Transaction.cs | 365 ++--- 98 files changed, 10413 insertions(+), 10311 deletions(-) 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/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.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/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/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..0955ccf93f 100644 --- a/src/Neo.Cryptography.BLS12_381/Fp.cs +++ b/src/Neo.Cryptography.BLS12_381/Fp.cs @@ -18,474 +18,475 @@ 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() + { + var output = string.Empty; + foreach (var b in ToArray()) + output += b.ToString("x2"); - return "0x" + output; - } + 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; - - (_, 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; - } + 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 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; - } + (_, 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 Invert() - { - if (!TryInvert(out Fp result)) - throw new DivideByZeroException(); - return result; - } + return borrow == 0; + } - public bool TryInvert(out Fp result) - { - // Exponentiate by p - 2 - result = this.PowVartime(P_2); + 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; + } - // Why not return before Pow() if IsZero? - // Because we want to run the method in a constant time. - return !IsZero; - } + public Fp Invert() + { + if (!TryInvert(out Fp result)) + throw new DivideByZeroException(); + return result; + } - 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; - } + public bool TryInvert(out Fp result) + { + // Exponentiate by p - 2 + result = this.PowVartime(P_2); - 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(); - } + // 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) - { - 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; - } + private Fp SubtractP() + { + Fp result; + ReadOnlySpan s = GetSpanU64(); + Span r = result.GetSpanU64(); + ulong borrow; - public static Fp operator -(in Fp a, in Fp b) - { - return -b + a; - } + (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 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, 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(); + } - Fp result; - ReadOnlySpan au = MemoryMarshal.Cast(a); - ReadOnlySpan bu = MemoryMarshal.Cast(b); - Span u = result.GetSpanU64(); + 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; + } - for (int j = 0; j < 6; j++) + public static Fp operator -(in Fp a, in Fp b) { - ulong carry; + return -b + a; + } + + 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."); + + Fp result; + ReadOnlySpan au = MemoryMarshal.Cast(a); + ReadOnlySpan bu = MemoryMarshal.Cast(b); + Span u = result.GetSpanU64(); - 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++) + 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/Scalar.cs b/src/Neo.Cryptography.BLS12_381/Scalar.cs index a4e93efe55..a26b7bf67d 100644 --- a/src/Neo.Cryptography.BLS12_381/Scalar.cs +++ b/src/Neo.Cryptography.BLS12_381/Scalar.cs @@ -19,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."); - - ReadOnlySpan d = MemoryMarshal.Cast(data); - return d[0] * R2 + d[1] * R3; - } + 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 FromRaw(ReadOnlySpan data) - { - if (data.Length != SizeL) - throw new FormatException($"The argument `{nameof(data)}` must contain {SizeL} entries."); + ReadOnlySpan d = MemoryMarshal.Cast(data); + return d[0] * R2 + d[1] * R3; + } - ReadOnlySpan span = MemoryMarshal.Cast(data); - return span[0] * R2; - } + public static Scalar FromRaw(ReadOnlySpan data) + { + if (data.Length != SizeL) + throw new FormatException($"The argument `{nameof(data)}` must contain {SizeL} entries."); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReadOnlySpan GetSpan() - { - return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); - } + ReadOnlySpan span = MemoryMarshal.Cast(data); + return span[0] * R2; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Span GetSpanU64() - { - return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan GetSpan() + { + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this), 1)); + } - public override string ToString() - { - var bytes = ToArray(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetSpanU64() + { + return MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this), 1)); + } - StringBuilder sb = new(2 + (bytes.Length * 2)); - sb.Append("0x"); - sb.Append(bytes.ToHexString(true)); - 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.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.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/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..7a6955e414 100644 --- a/src/Neo.Json/JToken.cs +++ b/src/Neo.Json/JToken.cs @@ -12,294 +12,295 @@ 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 + private static JToken? Read(ref Utf8JsonReader reader, bool skipReading = false) { - 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(), - }; - } + 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(), + }; + } - private static JArray ReadArray(ref Utf8JsonReader reader) - { - JArray array = new(); - while (reader.Read()) + private static JArray ReadArray(ref Utf8JsonReader reader) { - switch (reader.TokenType) + JArray array = new(); + while (reader.Read()) { - case JsonTokenType.EndArray: - return array; - default: - array.Add(Read(ref reader, skipReading: true)); - break; + switch (reader.TokenType) + { + case JsonTokenType.EndArray: + return array; + default: + array.Add(Read(ref reader, skipReading: true)); + break; + } } + throw new FormatException(); } - throw new FormatException(); - } - private static JObject ReadObject(ref Utf8JsonReader reader) - { - JObject obj = new(); - while (reader.Read()) + private static JObject ReadObject(ref Utf8JsonReader reader) { - switch (reader.TokenType) + JObject obj = new(); + while (reader.Read()) { - 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(); + switch (reader.TokenType) + { + 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(); } - throw new FormatException(); - } - private static string ReadString(ref Utf8JsonReader reader) - { - try + private static string ReadString(ref Utf8JsonReader reader) { - return reader.GetString()!; + try + { + return reader.GetString()!; + } + catch (InvalidOperationException ex) + { + throw new FormatException(ex.Message, ex); + } } - catch (InvalidOperationException ex) + + /// + /// Encode the current JSON token into a byte array. + /// + /// Indicates whether indentation is required. + /// The encoded JSON token. + public byte[] ToByteArray(bool indented) { - throw new FormatException(ex.Message, ex); + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms, new JsonWriterOptions + { + Indented = indented, + SkipValidation = true + }); + Write(writer); + writer.Flush(); + return ms.ToArray(); } - } - /// - /// 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 . + /// + /// The encoded JSON token. + public override string ToString() { - Indented = indented, - SkipValidation = true - }); - Write(writer); - writer.Flush(); - return ms.ToArray(); - } - - /// - /// Encode the current JSON token into a . - /// - /// The encoded JSON token. - public override string ToString() - { - return ToString(false); - } + return ToString(false); + } - /// - /// 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)); - } + /// + /// 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)); + } - internal abstract void Write(Utf8JsonWriter writer); + internal abstract void Write(Utf8JsonWriter writer); - public abstract JToken Clone(); + public abstract JToken Clone(); - 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; - } + 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; + } - 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/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/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/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/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/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/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/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..7b270ec81b 100644 --- a/src/Plugins/SQLiteWallet/SQLiteWallet.cs +++ b/src/Plugins/SQLiteWallet/SQLiteWallet.cs @@ -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/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/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.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.SmartContract.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs index 9a8b87da9a..5f2ad3c18f 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.SmartContract.cs @@ -30,205 +30,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..801938903a 100644 --- a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs +++ b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs @@ -24,377 +24,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 +405,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.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/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); + } } } From 44d8edd1086b0f109b65cabf2600cd2ebc1e4070 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 8 Nov 2024 15:27:29 -0500 Subject: [PATCH 33/53] Update src/Plugins/RestServer/Helpers/ScriptHelper.cs Co-authored-by: Owen <38493437+superboyiii@users.noreply.github.com> --- src/Plugins/RestServer/Helpers/ScriptHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/RestServer/Helpers/ScriptHelper.cs b/src/Plugins/RestServer/Helpers/ScriptHelper.cs index d592ef8708..8b641c12c0 100644 --- a/src/Plugins/RestServer/Helpers/ScriptHelper.cs +++ b/src/Plugins/RestServer/Helpers/ScriptHelper.cs @@ -35,7 +35,7 @@ public static bool InvokeMethod(ProtocolSettings protocolSettings, DataCache sna public static ApplicationEngine InvokeMethod(ProtocolSettings protocolSettings, DataCache snapshot, UInt160 scriptHash, string method, ContractParameter[] args, Signer[]? signers, out byte[] script) { using var scriptBuilder = new ScriptBuilder(); - scriptBuilder.EmitDynamicCall(scriptHash, method, CallFlags.ReadOnly, args); + scriptBuilder.EmitDynamicCall(scriptHash, method, CallFlags.All, args); script = scriptBuilder.ToArray(); var tx = signers == null ? null : new Transaction { From fb3adf7217b4bcdca7d83718a023e1cb566f014a Mon Sep 17 00:00:00 2001 From: nan01ab Date: Sat, 9 Nov 2024 19:03:01 +0800 Subject: [PATCH 34/53] A simpler impl for `ByteArrayEqualityComparer` and remove redundant impl (#3562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * A better and simpler impl for ByteArrayEqualityComparer A better and simpler impl for ByteArrayEqualityComparer * use HashCode.Combine * Move to Extensions --------- Co-authored-by: Vitor Nazário Coelho Co-authored-by: Jimmy Co-authored-by: Shargon --- .../ByteArrayEqualityComparer.cs | 35 +++++++++++ src/Neo.Extensions/ByteExtensions.cs | 26 +++++++++ src/Neo.Extensions/Neo.Extensions.csproj | 1 + src/Neo.IO/ByteArrayEqualityComparer.cs | 58 ------------------- src/Neo/Cryptography/Helper.cs | 26 --------- src/Neo/IO/Caching/ECPointCache.cs | 1 + src/Neo/Neo.csproj | 1 - src/Neo/SmartContract/StorageKey.cs | 2 +- .../Cryptography/MPTTrie/Trie.Proof.cs | 2 +- .../MPTTrie/IO/ByteArrayEqualityComparer.cs | 58 ------------------- .../UT_ByteArrayEqualityComparer.cs | 2 +- .../Neo.Extensions.Tests/UT_ByteExtensions.cs | 10 ++++ .../Cryptography/UT_Cryptography_Helper.cs | 7 --- tests/Neo.UnitTests/Ledger/UT_StorageKey.cs | 1 + 14 files changed, 77 insertions(+), 153 deletions(-) create mode 100644 src/Neo.Extensions/ByteArrayEqualityComparer.cs delete mode 100644 src/Neo.IO/ByteArrayEqualityComparer.cs delete mode 100644 src/Plugins/MPTTrie/IO/ByteArrayEqualityComparer.cs rename tests/{Neo.UnitTests/IO => Neo.Extensions.Tests}/UT_ByteArrayEqualityComparer.cs (98%) diff --git a/src/Neo.Extensions/ByteArrayEqualityComparer.cs b/src/Neo.Extensions/ByteArrayEqualityComparer.cs new file mode 100644 index 0000000000..9dbd2e5a37 --- /dev/null +++ b/src/Neo.Extensions/ByteArrayEqualityComparer.cs @@ -0,0 +1,35 @@ +// 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; +using System.Linq; + +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.SequenceEqual(y); + } + + public int GetHashCode(byte[] obj) + { + return obj.XxHash3_32(); + } + } +} diff --git a/src/Neo.Extensions/ByteExtensions.cs b/src/Neo.Extensions/ByteExtensions.cs index 73aed3e388..61cb5bde2e 100644 --- a/src/Neo.Extensions/ByteExtensions.cs +++ b/src/Neo.Extensions/ByteExtensions.cs @@ -10,6 +10,7 @@ // modifications are permitted. using System; +using System.IO.Hashing; using System.Runtime.CompilerServices; using System.Text; @@ -17,8 +18,33 @@ 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 . /// diff --git a/src/Neo.Extensions/Neo.Extensions.csproj b/src/Neo.Extensions/Neo.Extensions.csproj index f424dd0809..38cbf346fb 100644 --- a/src/Neo.Extensions/Neo.Extensions.csproj +++ b/src/Neo.Extensions/Neo.Extensions.csproj @@ -11,6 +11,7 @@ + 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/Cryptography/Helper.cs b/src/Neo/Cryptography/Helper.cs index 761073463b..f398612e61 100644 --- a/src/Neo/Cryptography/Helper.cs +++ b/src/Neo/Cryptography/Helper.cs @@ -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/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/Neo.csproj b/src/Neo/Neo.csproj index 89c200e31b..7c6478c33e 100644 --- a/src/Neo/Neo.csproj +++ b/src/Neo/Neo.csproj @@ -16,7 +16,6 @@ - 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/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/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..7f4a58bc0d 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 { @@ -34,6 +37,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.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/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; From 1061dca60d0ddb898b98432c86499ac9067ec6ad Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Sat, 9 Nov 2024 09:30:49 -0500 Subject: [PATCH 35/53] Fixed names of labels (#3569) Co-authored-by: Jimmy --- .github/ISSUE_TEMPLATE/feature-or-enhancement-request.md | 2 +- .github/ISSUE_TEMPLATE/questions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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: '' --- From 233d0b03501a1af5e6a140721a67bb37de84ecc9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 11 Nov 2024 15:42:31 +0100 Subject: [PATCH 36/53] Added equals to `ContractMethodDescriptor` (#3570) * Some equals * Add UT * remove nullable * Apply suggestions from code review * fix null check * Add review changes --------- Co-authored-by: Jimmy --- .../Manifest/ContractEventDescriptor.cs | 42 ++++++++++++++++- .../Manifest/ContractMethodDescriptor.cs | 45 ++++++++++++++++++- .../Manifest/ContractParameterDefinition.cs | 42 ++++++++++++++++- .../Manifest/UT_ContractManifest.cs | 17 +++++++ 4 files changed, 143 insertions(+), 3 deletions(-) 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/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/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() { From cf2b55999ab31309735b7128b97f1cc633b6ce8e Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Tue, 12 Nov 2024 17:07:02 -0500 Subject: [PATCH 37/53] [Add] `IEquatable` to WitnessCondition (#3572) * Add `IEquatable` to WitnessCondition * Added `null` check to `Equal(other)` * Fixed `Equals` compacked method * Added `AggressiveInlining` * Fixed `==` and `!=` * Remove size from Equals, fix NotCondition, remove ( ) --------- Co-authored-by: Fernando Diaz Toledano --- .../P2P/Payloads/Conditions/AndCondition.cs | 44 ++- .../Payloads/Conditions/BooleanCondition.cs | 45 ++- .../Conditions/CalledByContractCondition.cs | 45 ++- .../Conditions/CalledByEntryCondition.cs | 43 ++- .../Conditions/CalledByGroupCondition.cs | 45 ++- .../P2P/Payloads/Conditions/GroupCondition.cs | 45 ++- .../P2P/Payloads/Conditions/NotCondition.cs | 44 ++- .../P2P/Payloads/Conditions/OrCondition.cs | 44 ++- .../Conditions/ScriptHashCondition.cs | 45 ++- .../Payloads/Conditions/WitnessCondition.cs | 23 ++ .../P2P/Payloads/UT_WitnessContition.cs | 295 ++++++++++++++++++ 11 files changed, 709 insertions(+), 9 deletions(-) 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..4ce1e2dc1f 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.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 CalledByContractCondition : WitnessCondition + public class CalledByContractCondition : WitnessCondition, IEquatable { /// /// The script hash to be checked. @@ -28,6 +30,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 +86,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..ac233a156a 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs @@ -16,12 +16,14 @@ 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 +33,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 +91,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..18e94fbc40 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs @@ -16,12 +16,14 @@ 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 +33,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 +91,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..fd813c7230 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs @@ -16,13 +16,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 +33,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 +91,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..a9cf1de0e9 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.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 ScriptHashCondition : WitnessCondition + public class ScriptHashCondition : WitnessCondition, IEquatable { /// /// The script hash to be checked. @@ -28,6 +30,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 +86,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/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() { From e6fa071257cfab32ce8a0202b643379db2853a7f Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 13 Nov 2024 08:22:11 -0500 Subject: [PATCH 38/53] [Add] `IEquatable` to WitnessRule (#3573) * Add `IEquatable` to WitnessRule * Added `null` check to `Equals(other)` * Added `AggressiveInlining` and compacked `Equals` * Fixed `==` and `!-` * Update src/Neo/Network/P2P/Payloads/WitnessRule.cs --------- Co-authored-by: Shargon --- src/Neo/Network/P2P/Payloads/WitnessRule.cs | 42 ++++++++++- .../Network/P2P/Payloads/UT_WitnessRule.cs | 72 +++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 tests/Neo.UnitTests/Network/P2P/Payloads/UT_WitnessRule.cs diff --git a/src/Neo/Network/P2P/Payloads/WitnessRule.cs b/src/Neo/Network/P2P/Payloads/WitnessRule.cs index 8552f0798e..08ab3911b2 100644 --- a/src/Neo/Network/P2P/Payloads/WitnessRule.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRule.cs @@ -17,13 +17,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,6 +38,27 @@ 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(); @@ -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/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)); + } + } +} From 83289289c8c7bdf87228e21fb6f34dd9c1626d65 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Fri, 15 Nov 2024 01:05:00 +0800 Subject: [PATCH 39/53] make Exception message more clear in Neo.Json (#3579) --- src/Neo.Json/JNumber.cs | 5 +-- src/Neo.Json/JPathToken.cs | 68 ++++++++++++++++++++++++-------------- src/Neo.Json/JToken.cs | 17 ++++++---- 3 files changed, 58 insertions(+), 32 deletions(-) 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/JToken.cs b/src/Neo.Json/JToken.cs index 7a6955e414..a01beba974 100644 --- a/src/Neo.Json/JToken.cs +++ b/src/Neo.Json/JToken.cs @@ -171,7 +171,7 @@ public int GetInt32() JsonTokenType.StartObject => ReadObject(ref reader), JsonTokenType.String => ReadString(ref reader), JsonTokenType.True => true, - _ => throw new FormatException(), + _ => throw new FormatException($"Unexpected token {reader.TokenType}"), }; } @@ -189,7 +189,7 @@ private static JArray ReadArray(ref Utf8JsonReader reader) break; } } - throw new FormatException(); + throw new FormatException("Unterminated array"); } private static JObject ReadObject(ref Utf8JsonReader reader) @@ -203,15 +203,17 @@ private static JObject ReadObject(ref Utf8JsonReader reader) return obj; case JsonTokenType.PropertyName: string name = ReadString(ref reader); - if (obj.Properties.ContainsKey(name)) throw new FormatException(); + 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(); + throw new FormatException($"Unexpected token {reader.TokenType}"); } } - throw new FormatException(); + throw new FormatException("Unterminated object"); } private static string ReadString(ref Utf8JsonReader reader) @@ -271,9 +273,12 @@ 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(); + if (first.Type != JPathTokenType.Root) + throw new FormatException($"Unexpected token {first.Type}"); + JPathToken.ProcessJsonPath(ref objects, tokens); return objects; } From 8d946b419e4e3f0851be81e2c902d891ca6a3156 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Fri, 15 Nov 2024 16:08:36 +0800 Subject: [PATCH 40/53] use MemoryExtenions.SequenceEqual for better performance (#3578) Co-authored-by: Christopher Schuchardt --- src/Neo.Cryptography.BLS12_381/Fp.cs | 7 ++----- src/Neo.Extensions/ByteArrayEqualityComparer.cs | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Neo.Cryptography.BLS12_381/Fp.cs b/src/Neo.Cryptography.BLS12_381/Fp.cs index 0955ccf93f..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; @@ -152,11 +153,7 @@ public bool TryWrite(Span buffer) public override string ToString() { - var output = string.Empty; - foreach (var b in ToArray()) - output += b.ToString("x2"); - - return "0x" + output; + return "0x" + ToArray().ToHexString(); } public bool LexicographicallyLargest() diff --git a/src/Neo.Extensions/ByteArrayEqualityComparer.cs b/src/Neo.Extensions/ByteArrayEqualityComparer.cs index 9dbd2e5a37..789e58758e 100644 --- a/src/Neo.Extensions/ByteArrayEqualityComparer.cs +++ b/src/Neo.Extensions/ByteArrayEqualityComparer.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Neo.Extensions { @@ -24,7 +23,7 @@ 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.SequenceEqual(y); + return x.AsSpan().SequenceEqual(y.AsSpan()); } public int GetHashCode(byte[] obj) From 849b2c8d63e53a1311b529e3764067f55d285404 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Sun, 17 Nov 2024 22:01:35 -0500 Subject: [PATCH 41/53] [Remove] obsolete `WsServer` (#3582) * Remove WSServer obsolete * Revert `global.json` * Update ServerCapability.cs --------- Co-authored-by: Shargon --- .../Network/P2P/Capabilities/NodeCapability.cs | 4 +--- .../P2P/Capabilities/NodeCapabilityType.cs | 8 -------- .../P2P/Capabilities/ServerCapability.cs | 6 ++---- .../P2P/Capabilities/UT_ServerCapability.cs | 18 ++---------------- 4 files changed, 5 insertions(+), 31 deletions(-) diff --git a/src/Neo/Network/P2P/Capabilities/NodeCapability.cs b/src/Neo/Network/P2P/Capabilities/NodeCapability.cs index c47f238a4f..737f2873be 100644 --- a/src/Neo/Network/P2P/Capabilities/NodeCapability.cs +++ b/src/Neo/Network/P2P/Capabilities/NodeCapability.cs @@ -56,9 +56,7 @@ public static NodeCapability DeserializeFrom(ref MemoryReader reader) NodeCapabilityType type = (NodeCapabilityType)reader.ReadByte(); NodeCapability capability = type switch { -#pragma warning disable CS0612 // Type or member is obsolete - NodeCapabilityType.TcpServer or NodeCapabilityType.WsServer => new ServerCapability(type), -#pragma warning restore CS0612 // Type or member is obsolete + NodeCapabilityType.TcpServer => new ServerCapability(type), NodeCapabilityType.FullNode => new FullNodeCapability(), _ => throw new FormatException(), }; diff --git a/src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs b/src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs index 419d086fa9..68305380e9 100644 --- a/src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs +++ b/src/Neo/Network/P2P/Capabilities/NodeCapabilityType.cs @@ -9,8 +9,6 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - namespace Neo.Network.P2P.Capabilities { /// @@ -25,12 +23,6 @@ public enum NodeCapabilityType : byte /// TcpServer = 0x01, - /// - /// Indicates that the node is listening on a WebSocket port. - /// - [Obsolete] - WsServer = 0x02, - #endregion #region Others diff --git a/src/Neo/Network/P2P/Capabilities/ServerCapability.cs b/src/Neo/Network/P2P/Capabilities/ServerCapability.cs index e8c2d110df..820c4eff6d 100644 --- a/src/Neo/Network/P2P/Capabilities/ServerCapability.cs +++ b/src/Neo/Network/P2P/Capabilities/ServerCapability.cs @@ -32,13 +32,11 @@ public class ServerCapability : NodeCapability /// /// Initializes a new instance of the class. /// - /// The type of the . It must be or + /// The type of the . It must be /// The port that the node is listening on. public ServerCapability(NodeCapabilityType type, ushort port = 0) : base(type) { -#pragma warning disable CS0612 // Type or member is obsolete - if (type != NodeCapabilityType.TcpServer && type != NodeCapabilityType.WsServer) -#pragma warning restore CS0612 // Type or member is obsolete + if (type != NodeCapabilityType.TcpServer) { throw new ArgumentException(nameof(type)); } diff --git a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs index b113d59da7..5551daae65 100644 --- a/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs +++ b/tests/Neo.UnitTests/Network/P2P/Capabilities/UT_ServerCapability.cs @@ -25,18 +25,12 @@ public void Size_Get() { var test = new ServerCapability(NodeCapabilityType.TcpServer) { Port = 1 }; test.Size.Should().Be(3); - -#pragma warning disable CS0612 // Type or member is obsolete - test = new ServerCapability(NodeCapabilityType.WsServer) { Port = 2 }; -#pragma warning restore CS0612 // Type or member is obsolete - test.Size.Should().Be(3); } [TestMethod] public void DeserializeAndSerialize() { -#pragma warning disable CS0612 // Type or member is obsolete - var test = new ServerCapability(NodeCapabilityType.WsServer) { Port = 2 }; + var test = new ServerCapability(NodeCapabilityType.TcpServer) { Port = 2 }; var buffer = test.ToArray(); var br = new MemoryReader(buffer); @@ -45,21 +39,13 @@ public void DeserializeAndSerialize() Assert.AreEqual(test.Port, clone.Port); Assert.AreEqual(test.Type, clone.Type); - clone = new ServerCapability(NodeCapabilityType.WsServer, 123); -#pragma warning restore CS0612 // Type or member is obsolete + clone = new ServerCapability(NodeCapabilityType.TcpServer, 123); br = new MemoryReader(buffer); ((ISerializable)clone).Deserialize(ref br); Assert.AreEqual(test.Port, clone.Port); Assert.AreEqual(test.Type, clone.Type); - clone = new ServerCapability(NodeCapabilityType.TcpServer, 123); - - Assert.ThrowsException(() => - { - var br2 = new MemoryReader(buffer); - ((ISerializable)clone).Deserialize(ref br2); - }); Assert.ThrowsException(() => { _ = new ServerCapability(NodeCapabilityType.FullNode); From ed97f09f5847f4ae2ee220b9b2ac93f1e0426814 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 18 Nov 2024 11:20:54 +0800 Subject: [PATCH 42/53] [Plugin LevelDB] First step of 3414 - Comments and style (#3575) * first step of 3414 * apply roman's comments * remvoe comment * format update * Update src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs --------- Co-authored-by: Shargon Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- .../LevelDBStore/IO/Data/LevelDB/DB.cs | 23 ++++ .../LevelDBStore/IO/Data/LevelDB/Iterator.cs | 21 ++++ .../LevelDBStore/IO/Data/LevelDB/Options.cs | 117 ++++++++++++------ .../IO/Data/LevelDB/ReadOptions.cs | 34 +++-- .../LevelDBStore/IO/Data/LevelDB/Snapshot.cs | 4 + .../IO/Data/LevelDB/WriteBatch.cs | 30 ++++- .../IO/Data/LevelDB/WriteOptions.cs | 28 ++++- .../LevelDBStore/Plugins/Storage/Snapshot.cs | 32 ++--- .../LevelDBStore/Plugins/Storage/Store.cs | 51 +++----- 9 files changed, 233 insertions(+), 107 deletions(-) diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs index 60e0e24e5a..4675af80d5 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs @@ -14,6 +14,10 @@ namespace Neo.IO.Data.LevelDB { + /// + /// 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. + /// public class DB : IDisposable { private IntPtr handle; @@ -37,12 +41,21 @@ public void Dispose() } } + /// + /// 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); 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); @@ -93,12 +106,22 @@ public static DB Open(string name, Options options) 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); 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); diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs index 6c025380fb..0aeaa8cf92 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs @@ -13,6 +13,9 @@ namespace Neo.IO.Data.LevelDB { + /// + /// An iterator yields a sequence of key/value pairs from a database. + /// public class Iterator : IDisposable { private IntPtr handle; @@ -37,6 +40,10 @@ public void Dispose() } } + /// + /// Return the key for the current entry. + /// REQUIRES: Valid() + /// public byte[] Key() { IntPtr key = Native.leveldb_iter_key(handle, out UIntPtr length); @@ -44,6 +51,11 @@ public byte[] Key() 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); @@ -56,6 +68,11 @@ public void Prev() CheckError(); } + /// + /// 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[] target) { Native.leveldb_iter_seek(handle, target, (UIntPtr)target.Length); @@ -66,6 +83,10 @@ public void SeekToFirst() 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); diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs index 989987eeed..37d2de8453 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs @@ -13,81 +13,126 @@ namespace Neo.IO.Data.LevelDB { + /// + /// 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 { public static readonly Options Default = new Options(); internal readonly IntPtr handle = 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); } } + /// + /// 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 Compression { - set - { - Native.leveldb_options_set_compression(handle, value); - } + set { Native.leveldb_options_set_compression(handle, value); } } public IntPtr FilterPolicy { - set - { - Native.leveldb_options_set_filter_policy(handle, value); - } + set { Native.leveldb_options_set_filter_policy(handle, value); } } ~Options() diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs index 727ae9f02a..88c4a0a68d 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs @@ -13,33 +13,43 @@ namespace Neo.IO.Data.LevelDB { + /// + /// Options that control read operations. + /// public class ReadOptions { public static readonly ReadOptions Default = new ReadOptions(); internal readonly IntPtr handle = 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() diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs index 14280fbc8f..d05839300c 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs @@ -13,6 +13,10 @@ namespace Neo.IO.Data.LevelDB { + /// + /// A Snapshot is an immutable object and can therefore be safely + /// accessed from multiple threads without any external synchronization. + /// public class Snapshot : IDisposable { internal IntPtr db, handle; diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs index ad82dad450..6e2803314f 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs @@ -13,6 +13,18 @@ namespace Neo.IO.Data.LevelDB { + /// + /// 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 { internal readonly IntPtr handle = Native.leveldb_writebatch_create(); @@ -22,19 +34,29 @@ public class WriteBatch Native.leveldb_writebatch_destroy(handle); } + /// + /// Clear all updates buffered in this batch. + /// public void Clear() { Native.leveldb_writebatch_clear(handle); } - public void Delete(byte[] key) + /// + /// Store the mapping "key->value" in the database. + /// + public void Put(byte[] key, byte[] value) { - Native.leveldb_writebatch_delete(handle, key, (UIntPtr)key.Length); + Native.leveldb_writebatch_put(handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); } - public void Put(byte[] key, byte[] value) + /// + /// If the database contains a mapping for "key", erase it. + /// Else do nothing. + /// + public void Delete(byte[] key) { - Native.leveldb_writebatch_put(handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); + Native.leveldb_writebatch_delete(handle, key, (UIntPtr)key.Length); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs index 48915ba480..9fbf6af20b 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs @@ -13,19 +13,35 @@ namespace Neo.IO.Data.LevelDB { + /// + /// Options that control write operations. + /// public class WriteOptions { - 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(); + /// + /// 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() diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index f66164285b..4f7c73d820 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -18,57 +18,57 @@ namespace Neo.Plugins.Storage { internal class Snapshot : ISnapshot { - 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 _options; + private readonly WriteBatch _batch; public Snapshot(DB db) { - this.db = db; - snapshot = db.GetSnapshot(); - options = new ReadOptions { FillCache = false, Snapshot = snapshot }; - batch = new WriteBatch(); + _db = db; + _snapshot = db.GetSnapshot(); + _options = new ReadOptions { FillCache = false, Snapshot = _snapshot }; + _batch = new WriteBatch(); } public void Commit() { - db.Write(WriteOptions.Default, batch); + _db.Write(WriteOptions.Default, _batch); } public void Delete(byte[] key) { - batch.Delete(key); + _batch.Delete(key); } public void Dispose() { - snapshot.Dispose(); + _snapshot.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(_options, prefix, direction, (k, v) => (k, v)); } public void Put(byte[] key, byte[] value) { - batch.Put(key, value); + _batch.Put(key, value); } public bool Contains(byte[] key) { - return db.Contains(options, key); + return _db.Contains(_options, key); } public byte[] TryGet(byte[] key) { - return db.Get(options, key); + return _db.Get(_options, key); } public bool TryGet(byte[] key, out byte[] value) { - value = db.Get(options, key); + value = _db.Get(_options, key); return value != null; } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs index 175b71a8fe..337a598a61 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -17,57 +17,42 @@ namespace Neo.Plugins.Storage { internal class Store : IStore { - private readonly DB db; + private readonly DB _db; public Store(string path) { - db = DB.Open(path, new Options { CreateIfMissing = true, FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15) }); + _db = DB.Open(path, new Options { CreateIfMissing = true, FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15) }); } public void Delete(byte[] key) { - db.Delete(WriteOptions.Default, key); + _db.Delete(WriteOptions.Default, key); } - public void Dispose() - { - db.Dispose(); - } - - public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) - { - return db.Seek(ReadOptions.Default, prefix, direction, (k, v) => (k, v)); - } + public void Dispose() => _db.Dispose(); - 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, (k, v) => (k, v)); } } From a72a0c951ded7b8c8502dff28464cf82f6c04024 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Mon, 18 Nov 2024 14:15:48 +0800 Subject: [PATCH 43/53] [Core VM] update exception message in the vm to make it more clear. (#3577) * update exception message in the vm to make it more clear. * fix exception type * apply anna's comment * remvoe extra index * keep fixing extra index * check everywhere what anna has suggested * remove more redundent value detail. and change value format * avoid object.tostring fail, apply shargon comment --------- Co-authored-by: Shargon Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo.VM/EvaluationStack.cs | 6 ++-- src/Neo.VM/ExecutionEngine.cs | 2 +- src/Neo.VM/ExecutionEngineLimits.cs | 4 +-- src/Neo.VM/JumpTable/JumpTable.Compound.cs | 34 +++++++++++----------- src/Neo.VM/JumpTable/JumpTable.Slot.cs | 4 +-- src/Neo.VM/JumpTable/JumpTable.Splice.cs | 24 +++++++-------- src/Neo.VM/Types/Array.cs | 8 ++--- src/Neo.VM/Types/ByteString.cs | 6 ++-- src/Neo.VM/Types/Integer.cs | 2 +- src/Neo.VM/Types/InteropInterface.cs | 2 +- src/Neo.VM/Types/Map.cs | 16 +++++----- src/Neo.VM/Types/Null.cs | 2 +- src/Neo.VM/Types/Struct.cs | 6 ++-- src/Neo.VM/Utility.cs | 2 +- 14 files changed, 59 insertions(+), 59 deletions(-) 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/Types/Array.cs b/src/Neo.VM/Types/Array.cs index 903613c228..e15bb2a8dc 100644 --- a/src/Neo.VM/Types/Array.cs +++ b/src/Neo.VM/Types/Array.cs @@ -95,7 +95,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 +109,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 +150,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 +160,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/ByteString.cs b/src/Neo.VM/Types/ByteString.cs index d2f1ebee65..c354952f1a 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 @@ -85,7 +85,7 @@ public override bool GetBoolean() 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..b53bac3238 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; } 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..7f7b726d63 100644 --- a/src/Neo.VM/Types/Map.cs +++ b/src/Neo.VM/Types/Map.cs @@ -40,14 +40,14 @@ public StackItem this[PrimitiveType key] 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 +93,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 +114,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 +151,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 +178,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..5787d4d0bc 100644 --- a/src/Neo.VM/Types/Null.cs +++ b/src/Neo.VM/Types/Null.cs @@ -26,7 +26,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; } 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/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; From 2510282a50533396320e899323081616ee49b4f3 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Tue, 19 Nov 2024 15:56:48 +0800 Subject: [PATCH 44/53] Add more info to deserialization exceptions (#3585) * add more info to deserialization exceptions * Update src/Neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs --------- Co-authored-by: Jimmy Co-authored-by: Shargon --- src/Neo/Network/P2P/Message.cs | 2 +- src/Neo/Network/P2P/Payloads/ExtensiblePayload.cs | 9 ++++++--- .../Network/P2P/Payloads/GetBlockByIndexPayload.cs | 2 +- src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs | 3 ++- src/Neo/Network/P2P/Payloads/OracleResponse.cs | 3 ++- src/Neo/Network/P2P/Payloads/Transaction.cs | 13 +++++++++---- .../Network/P2P/Payloads/TransactionAttribute.cs | 7 ++++--- src/Neo/Network/P2P/Payloads/WitnessRule.cs | 5 ++--- 8 files changed, 27 insertions(+), 17 deletions(-) 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/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..254287135f 100644 --- a/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs +++ b/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs @@ -51,7 +51,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/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/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 08ab3911b2..515d234666 100644 --- a/src/Neo/Network/P2P/Payloads/WitnessRule.cs +++ b/src/Neo/Network/P2P/Payloads/WitnessRule.cs @@ -63,7 +63,7 @@ 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); } @@ -81,9 +81,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() { From 80fc4fdc9a385b04916f140620b550dd75ca11c7 Mon Sep 17 00:00:00 2001 From: nan01ab Date: Wed, 20 Nov 2024 10:16:40 +0800 Subject: [PATCH 45/53] Remove some ToArrays to reduce memory allocations (#3584) * fea: remove some ToArrays to reduce memory allocations * avoid linq --------- Co-authored-by: Jimmy --- .../Collections/CollectionExtensions.cs | 82 ++++++++++++++++++ src/Neo/Network/P2P/Payloads/InvPayload.cs | 8 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 2 +- src/Neo/Network/P2P/TaskManager.cs | 27 ++---- .../Collections/UT_CollectionExtensions.cs | 83 +++++++++++++++++++ 5 files changed, 177 insertions(+), 25 deletions(-) create mode 100644 src/Neo.Extensions/Collections/CollectionExtensions.cs create mode 100644 tests/Neo.Extensions.Tests/Collections/UT_CollectionExtensions.cs 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/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/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/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/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]); + } + } +} From 842fa5288b0dfe424be9b26b6322aa8627550868 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 20 Nov 2024 03:16:24 -0500 Subject: [PATCH 46/53] [Add] `IEquatable` to Signer (#3571) * Added IEquatable to Signer * Added `null` check to unit tests * Added better testing of Signer * Apply suggestions from code review * Added WitnessRules checking to the unit tests * Added `null` check to `Equals(other)` * Added `AggressiveInlining` and Compacked `Equals` * Update src/Neo/Network/P2P/Payloads/Signer.cs * Fixed null checking --------- Co-authored-by: Shargon Co-authored-by: Jimmy Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo/Network/P2P/Payloads/Signer.cs | 46 ++++++++++- .../Network/P2P/Payloads/UT_Signers.cs | 79 +++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/Neo/Network/P2P/Payloads/Signer.cs b/src/Neo/Network/P2P/Payloads/Signer.cs index 40496dfe74..122e86f6ee 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,31 @@ 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; + return Account == other.Account && + Scopes == other.Scopes && + AllowedContracts.AsSpan().SequenceEqual(other.AllowedContracts.AsSpan()) && + AllowedGroups.AsSpan().SequenceEqual(other.AllowedGroups.AsSpan()) && + Rules.AsEnumerable().SequenceEqual(other.Rules.AsEnumerable()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + { + if (obj == null) return false; + 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(); @@ -202,5 +228,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/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() { From 456f168f5fed4d55bf07e682418f5aa915b67bc9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 20 Nov 2024 22:55:50 +0100 Subject: [PATCH 47/53] Optimize Signer equals (#3588) * Fix equals * Remove using --- src/Neo/Network/P2P/Payloads/Signer.cs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Neo/Network/P2P/Payloads/Signer.cs b/src/Neo/Network/P2P/Payloads/Signer.cs index 122e86f6ee..eadc623c0a 100644 --- a/src/Neo/Network/P2P/Payloads/Signer.cs +++ b/src/Neo/Network/P2P/Payloads/Signer.cs @@ -72,18 +72,26 @@ public bool Equals(Signer other) { if (ReferenceEquals(this, other)) return true; + if (other is null) return false; - return Account == other.Account && - Scopes == other.Scopes && - AllowedContracts.AsSpan().SequenceEqual(other.AllowedContracts.AsSpan()) && - AllowedGroups.AsSpan().SequenceEqual(other.AllowedGroups.AsSpan()) && - Rules.AsEnumerable().SequenceEqual(other.Rules.AsEnumerable()); + 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) { - if (obj == null) return false; return obj is Signer signerObj && Equals(signerObj); } @@ -137,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, @@ -146,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, @@ -155,7 +163,7 @@ public IEnumerable GetAllRules() } if (Scopes.HasFlag(WitnessScope.WitnessRules)) { - foreach (WitnessRule rule in Rules) + foreach (var rule in Rules) yield return rule; } } From cd6667ef7d46a0154e1f950fac94128ca3469382 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 20 Nov 2024 17:08:29 -0500 Subject: [PATCH 48/53] Lower Timeout for the Akka System (#3587) * Lower Timeout for the Akka System * Update src/Neo/NeoSystem.cs Co-authored-by: Shargon --------- Co-authored-by: Shargon --- src/Neo/NeoSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)); } /// From a6fd680dc01935ed2b45a2bfb316f5abba0edfcb Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 21 Nov 2024 16:15:41 +0800 Subject: [PATCH 49/53] [Plugin LevelDB] Second step of 3414 - GC and Easy Management (#3586) * second part of the pr 3414 * Apply suggestions from code review * Update src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs * Fix Handle access --------- Co-authored-by: Shargon --- .../LevelDBStore/IO/Data/LevelDB/DB.cs | 41 +++++--------- .../LevelDBStore/IO/Data/LevelDB/Iterator.cs | 34 +++++------ .../IO/Data/LevelDB/LevelDBHandle.cs | 56 +++++++++++++++++++ .../LevelDBStore/IO/Data/LevelDB/Options.cs | 29 +++++----- .../IO/Data/LevelDB/ReadOptions.cs | 19 +++---- .../LevelDBStore/IO/Data/LevelDB/Snapshot.cs | 16 +++--- .../IO/Data/LevelDB/WriteBatch.cs | 20 +++---- .../IO/Data/LevelDB/WriteOptions.cs | 12 ++-- 8 files changed, 131 insertions(+), 96 deletions(-) create mode 100644 src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs index 4675af80d5..ecb79344cc 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs @@ -18,26 +18,15 @@ namespace Neo.IO.Data.LevelDB /// 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. /// - public class DB : IDisposable + public class DB : LevelDBHandle { - private IntPtr handle; + private DB(IntPtr handle) : base(handle) { } - /// - /// Return true if haven't got valid handle - /// - public bool IsDisposed => handle == IntPtr.Zero; - - private DB(IntPtr handle) - { - this.handle = handle; - } - - public void Dispose() + protected override void FreeUnManagedObjects() { - if (handle != IntPtr.Zero) + if (Handle != IntPtr.Zero) { - Native.leveldb_close(handle); - handle = IntPtr.Zero; + Native.leveldb_close(Handle); } } @@ -48,7 +37,7 @@ public void Dispose() /// 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, (UIntPtr)key.Length, out var error); NativeHelper.CheckError(error); } @@ -58,7 +47,7 @@ public void Delete(WriteOptions options, byte[] key) /// 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, (UIntPtr)key.Length, out var length, out var error); try { NativeHelper.CheckError(error); @@ -72,7 +61,7 @@ public byte[] Get(ReadOptions options, byte[] key) 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, (UIntPtr)key.Length, out _, out var error); NativeHelper.CheckError(error); if (value != IntPtr.Zero) @@ -86,12 +75,12 @@ public bool Contains(ReadOptions options, byte[] key) public Snapshot GetSnapshot() { - return new Snapshot(handle); + return new Snapshot(Handle); } public Iterator NewIterator(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) @@ -101,9 +90,9 @@ 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); + return new DB(Handle); } /// @@ -112,7 +101,7 @@ public static DB Open(string name, Options options) /// 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, (UIntPtr)key.Length, value, (UIntPtr)value.Length, out var error); NativeHelper.CheckError(error); } @@ -124,13 +113,13 @@ public void Put(WriteOptions options, byte[] key, byte[] value) /// 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); } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs index 0aeaa8cf92..d3e36a3a9c 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs @@ -16,27 +16,21 @@ namespace Neo.IO.Data.LevelDB /// /// An iterator yields a sequence of key/value pairs from a database. /// - public class Iterator : IDisposable + public class Iterator : LevelDBHandle { - private IntPtr handle; - - internal Iterator(IntPtr handle) - { - this.handle = handle; - } + internal Iterator(IntPtr 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 != IntPtr.Zero) { - Native.leveldb_iter_destroy(handle); - handle = IntPtr.Zero; + Native.leveldb_iter_destroy(Handle); } } @@ -46,7 +40,7 @@ public void Dispose() /// 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); } @@ -58,13 +52,13 @@ public byte[] Key() /// 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(); } @@ -75,12 +69,12 @@ public void Prev() /// public void Seek(byte[] target) { - Native.leveldb_iter_seek(handle, target, (UIntPtr)target.Length); + Native.leveldb_iter_seek(Handle, target, (UIntPtr)target.Length); } public void SeekToFirst() { - Native.leveldb_iter_seek_to_first(handle); + Native.leveldb_iter_seek_to_first(Handle); } /// @@ -89,17 +83,17 @@ public void SeekToFirst() /// 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/LevelDBHandle.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs new file mode 100644 index 0000000000..2f8aa9e4db --- /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.Data.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/Options.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs index 37d2de8453..99c7fb2739 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs @@ -19,17 +19,18 @@ namespace Neo.IO.Data.LevelDB /// the setter methods for InfoLogger, Env, and Cache only "safe to clean up guarantee". Do not /// use Option object if throws. /// - public class Options + 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); } } /// @@ -37,7 +38,7 @@ public bool CreateIfMissing /// public bool ErrorIfExists { - set { Native.leveldb_options_set_error_if_exists(handle, value); } + set { Native.leveldb_options_set_error_if_exists(Handle, value); } } /// @@ -49,7 +50,7 @@ public bool ErrorIfExists /// 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 @@ -70,7 +71,7 @@ public bool ParanoidChecks /// 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); } } /// @@ -82,7 +83,7 @@ public int WriteBufferSize /// public int MaxOpenFiles { - set { Native.leveldb_options_set_max_open_files(handle, value); } + set { Native.leveldb_options_set_max_open_files(Handle, value); } } /// @@ -95,7 +96,7 @@ public int MaxOpenFiles /// public int BlockSize { - set { Native.leveldb_options_set_block_size(handle, (UIntPtr)value); } + set { Native.leveldb_options_set_block_size(Handle, (UIntPtr)value); } } /// @@ -107,7 +108,7 @@ public int BlockSize /// public int BlockRestartInterval { - set { Native.leveldb_options_set_block_restart_interval(handle, value); } + set { Native.leveldb_options_set_block_restart_interval(Handle, value); } } /// @@ -127,17 +128,17 @@ public int BlockRestartInterval /// public CompressionType Compression { - set { Native.leveldb_options_set_compression(handle, value); } + set { Native.leveldb_options_set_compression(Handle, value); } } public IntPtr 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 88c4a0a68d..9a1d48f76e 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs @@ -9,17 +9,16 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - namespace Neo.IO.Data.LevelDB { /// /// Options that control read operations. /// - public class ReadOptions + 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 @@ -27,7 +26,7 @@ public class ReadOptions /// public bool VerifyChecksums { - set { Native.leveldb_readoptions_set_verify_checksums(handle, value); } + set { Native.leveldb_readoptions_set_verify_checksums(Handle, value); } } /// @@ -37,7 +36,7 @@ public bool VerifyChecksums /// public bool FillCache { - set { Native.leveldb_readoptions_set_fill_cache(handle, value); } + set { Native.leveldb_readoptions_set_fill_cache(Handle, value); } } /// @@ -49,12 +48,12 @@ public bool FillCache /// 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 d05839300c..e9ca8d1ff6 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs @@ -17,22 +17,20 @@ namespace Neo.IO.Data.LevelDB /// A Snapshot is an immutable object and can therefore be safely /// accessed from multiple threads without any external synchronization. /// - public class Snapshot : IDisposable + public class Snapshot : LevelDBHandle { - internal IntPtr db, handle; + internal IntPtr _db; - internal Snapshot(IntPtr db) + internal Snapshot(IntPtr 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 != IntPtr.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 6e2803314f..7e9c04305b 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs @@ -25,21 +25,16 @@ namespace Neo.IO.Data.LevelDB /// batch.Put("key", "v2"); /// batch.Put("key", "v3"); /// - public class WriteBatch + public class WriteBatch : LevelDBHandle { - internal readonly IntPtr handle = Native.leveldb_writebatch_create(); - - ~WriteBatch() - { - Native.leveldb_writebatch_destroy(handle); - } + public WriteBatch() : base(Native.leveldb_writebatch_create()) { } /// /// Clear all updates buffered in this batch. /// public void Clear() { - Native.leveldb_writebatch_clear(handle); + Native.leveldb_writebatch_clear(Handle); } /// @@ -47,7 +42,7 @@ public void Clear() /// public void Put(byte[] key, byte[] value) { - Native.leveldb_writebatch_put(handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); + Native.leveldb_writebatch_put(Handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); } /// @@ -56,7 +51,12 @@ public void Put(byte[] key, byte[] value) /// public void Delete(byte[] key) { - Native.leveldb_writebatch_delete(handle, key, (UIntPtr)key.Length); + Native.leveldb_writebatch_delete(Handle, key, (UIntPtr)key.Length); + } + + protected override void FreeUnManagedObjects() + { + 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 9fbf6af20b..994a97d8a2 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs @@ -9,19 +9,17 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - namespace Neo.IO.Data.LevelDB { /// /// Options that control write operations. /// - public class WriteOptions + public class WriteOptions : LevelDBHandle { 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 @@ -41,12 +39,12 @@ public class WriteOptions /// 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); } } } From e34bfd8dc195ea5f4c2fb08b5fb76240d9ae6fae Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 21 Nov 2024 18:48:09 +0800 Subject: [PATCH 50/53] [Core VM] optmize the vm (#3590) * optmize the vm * update format * Update Script.cs * Apply suggestions from code review --------- Co-authored-by: Shargon --- src/Neo.VM/Script.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) 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); From af5242f4d0b67654031b1cfc823d6e13714448d2 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 22 Nov 2024 09:06:12 +0100 Subject: [PATCH 51/53] Add ` [MethodImpl(MethodImplOptions.AggressiveInlining)] (#3592) ` --- src/Neo.VM/Types/Array.cs | 2 ++ src/Neo.VM/Types/Boolean.cs | 2 ++ src/Neo.VM/Types/ByteString.cs | 2 ++ src/Neo.VM/Types/Integer.cs | 2 ++ src/Neo.VM/Types/Map.cs | 2 ++ src/Neo.VM/Types/Null.cs | 3 +++ 6 files changed, 13 insertions(+) diff --git a/src/Neo.VM/Types/Array.cs b/src/Neo.VM/Types/Array.cs index e15bb2a8dc..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 { 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 c354952f1a..110cf5c17f 100644 --- a/src/Neo.VM/Types/ByteString.cs +++ b/src/Neo.VM/Types/ByteString.cs @@ -77,12 +77,14 @@ internal bool Equals(StackItem? other, ref uint limits) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool GetBoolean() { if (Size > Integer.MaxSize) throw new InvalidCastException(); return GetSpan().NotZero(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override BigInteger GetInteger() { 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}."); diff --git a/src/Neo.VM/Types/Integer.cs b/src/Neo.VM/Types/Integer.cs index b53bac3238..618a170e19 100644 --- a/src/Neo.VM/Types/Integer.cs +++ b/src/Neo.VM/Types/Integer.cs @@ -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/Map.cs b/src/Neo.VM/Types/Map.cs index 7f7b726d63..9ea88ba4a9 100644 --- a/src/Neo.VM/Types/Map.cs +++ b/src/Neo.VM/Types/Map.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.VM.Types { @@ -37,6 +38,7 @@ 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) diff --git a/src/Neo.VM/Types/Null.cs b/src/Neo.VM/Types/Null.cs index 5787d4d0bc..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 { @@ -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; From b2cbfc05aa2f8a517f029ec5cec51efbafbb4548 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 25 Nov 2024 04:55:08 -0500 Subject: [PATCH 52/53] `[Move]` Part-6 Classes into Different Library - `Neo.Extensions` (#3410) * Part-1 `Neo.IO` - move * Part-2 * Added `BigInteger` to `Neo.Extensions` * Found more `BigInteger` * Added `ByteArray` to `Neo.Extensions` * Added `DateTime` Extensions to `Neo.Extensions` * Added `HashSetExtensions`, `HashSetExtensions2`, `IpAddressExtensions`, `AssemblyExtensions`, `StringExtensdions` Deleted `Helper.cs` file * Added `ICollection`, `Memory`, `String`, `Unsafe` extensions * Adding `using` * dotnet format * Added more Extensions * Move some methods * Added Tests * Added `tests` from `Part-2` * Added `tests` for `PART-4` * Added `tests` for `PART-5` * Dotnet format * dotnet format * Renamed ByteExtensions2 to ByteExtensions * Apply suggestions from code review --------- Co-authored-by: Shargon Co-authored-by: Jimmy Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo/Cryptography/Helper.cs | 2 +- src/Neo/Cryptography/MerkleTree.cs | 2 +- src/Neo/Extensions/ByteExtensions.cs | 66 ++++ .../Extensions/IO/BinaryReaderExtensions.cs | 79 +++++ .../Extensions/IO/BinaryWriterExtensions.cs | 137 ++++++++ .../Extensions/IO/ISerializableExtensions.cs | 33 ++ .../Extensions/IO/MemoryReaderExtensions.cs | 70 ++++ src/Neo/Extensions/MemoryExtensions.cs | 14 + src/Neo/Extensions/SpanExtensions.cs | 80 +++++ src/Neo/IO/Helper.cs | 329 ------------------ src/Neo/Network/P2P/Helper.cs | 2 +- .../Conditions/CalledByContractCondition.cs | 1 + .../Conditions/CalledByGroupCondition.cs | 1 + .../P2P/Payloads/Conditions/GroupCondition.cs | 1 + .../P2P/Payloads/Conditions/NotCondition.cs | 1 + .../Conditions/ScriptHashCondition.cs | 1 + src/Neo/Network/P2P/Payloads/Conflicts.cs | 1 + .../Network/P2P/Payloads/GetBlocksPayload.cs | 1 + src/Neo/Network/P2P/Payloads/Header.cs | 1 + src/Neo/Network/P2P/Payloads/WitnessRule.cs | 1 + src/Neo/Network/P2P/RemoteNode.cs | 1 + src/Neo/Persistence/SnapshotCache.cs | 2 +- src/Neo/SmartContract/ApplicationEngine.cs | 1 + src/Neo/SmartContract/BinarySerializer.cs | 1 + src/Neo/SmartContract/ContractState.cs | 2 +- .../SmartContract/Manifest/ContractGroup.cs | 2 +- .../Manifest/ContractPermission.cs | 2 +- .../Manifest/ContractPermissionDescriptor.cs | 2 +- .../Native/ContractManagement.cs | 2 +- src/Neo/SmartContract/Native/FungibleToken.cs | 2 +- .../SmartContract/Native/HashIndexState.cs | 2 +- .../SmartContract/Native/OracleContract.cs | 2 +- src/Neo/SmartContract/Native/OracleRequest.cs | 2 +- .../SmartContract/Native/RoleManagement.cs | 2 +- src/Neo/SmartContract/NotifyEventArgs.cs | 2 +- src/Neo/VM/Helper.cs | 1 + .../ApplicationLogs/Store/LogStorageStore.cs | 2 +- .../DBFTPlugin/Consensus/ConsensusContext.cs | 1 + .../DBFTPlugin/Messages/PrepareResponse.cs | 1 + .../MPTTrie/Cryptography/MPTTrie/Cache.cs | 2 +- .../MPTTrie/Cryptography/MPTTrie/Node.Hash.cs | 1 + src/Plugins/RpcServer/RpcServer.Node.cs | 2 +- src/Plugins/RpcServer/RpcServer.Wallet.cs | 2 +- src/Plugins/SQLiteWallet/SQLiteWallet.cs | 2 +- src/Plugins/StateService/Network/StateRoot.cs | 1 + src/Plugins/StateService/StatePlugin.cs | 2 +- .../StateService/Storage/StateSnapshot.cs | 2 +- src/Plugins/StorageDumper/StorageDumper.cs | 2 +- .../Trackers/NEP-11/Nep11BalanceKey.cs | 1 + .../Trackers/NEP-11/Nep11TransferKey.cs | 1 + .../Trackers/NEP-17/Nep17BalanceKey.cs | 1 + .../TokensTracker/Trackers/TokenBalance.cs | 1 + .../TokensTracker/Trackers/TokenTransfer.cs | 1 + .../Trackers/TokenTransferKey.cs | 1 + .../TokensTracker/Trackers/TrackerBase.cs | 1 + .../UT_StringExtensions.cs | 151 ++++++++ tests/Neo.Network.RPC.Tests/UT_RpcClient.cs | 1 + .../UT_LogReader.cs | 1 + .../UT_RpcServer.Blockchain.cs | 1 + .../UT_RpcServer.Node.cs | 1 + .../UT_RpcServer.SmartContract.cs | 1 + .../UT_RpcServer.Wallet.cs | 1 + .../Cryptography/UT_MerkleTree.cs | 1 + .../Extensions/NativeContractExtensions.cs | 1 + .../Neo.UnitTests/IO/Caching/UT_DataCache.cs | 1 + tests/Neo.UnitTests/IO/UT_IOHelper.cs | 101 +++--- .../P2P/Capabilities/UT_FullNodeCapability.cs | 1 + .../P2P/Capabilities/UT_ServerCapability.cs | 1 + .../Network/P2P/Payloads/UT_AddrPayload.cs | 1 + .../Network/P2P/Payloads/UT_Conflicts.cs | 1 + .../P2P/Payloads/UT_ExtensiblePayload.cs | 1 + .../P2P/Payloads/UT_FilterAddPayload.cs | 1 + .../P2P/Payloads/UT_FilterLoadPayload.cs | 1 + .../P2P/Payloads/UT_GetBlockByIndexPayload.cs | 1 + .../P2P/Payloads/UT_GetBlocksPayload.cs | 1 + .../Network/P2P/Payloads/UT_HeadersPayload.cs | 1 + .../P2P/Payloads/UT_HighPriorityAttribute.cs | 1 + .../Network/P2P/Payloads/UT_InvPayload.cs | 1 + .../P2P/Payloads/UT_MerkleBlockPayload.cs | 1 + .../Network/P2P/Payloads/UT_NotValidBefore.cs | 1 + .../Network/P2P/Payloads/UT_Transaction.cs | 4 +- tests/Neo.UnitTests/Network/P2P/UT_Message.cs | 1 + .../Network/P2P/UT_RemoteNode.cs | 1 + .../Persistence/UT_MemoryStore.cs | 1 + .../SmartContract/Native/UT_GasToken.cs | 1 + .../SmartContract/Native/UT_PolicyContract.cs | 1 + .../SmartContract/UT_MethodToken.cs | 1 + .../Neo.UnitTests/SmartContract/UT_NefFile.cs | 1 + .../SmartContract/UT_Syscalls.cs | 1 + tests/Neo.UnitTests/TestUtils.cs | 1 + tests/Neo.UnitTests/UT_Helper.cs | 72 ++++ .../Wallets/UT_Wallets_Helper.cs | 1 + 92 files changed, 844 insertions(+), 395 deletions(-) create mode 100644 src/Neo/Extensions/ByteExtensions.cs create mode 100644 src/Neo/Extensions/IO/BinaryReaderExtensions.cs create mode 100644 src/Neo/Extensions/IO/BinaryWriterExtensions.cs create mode 100644 src/Neo/Extensions/IO/ISerializableExtensions.cs create mode 100644 src/Neo/Extensions/IO/MemoryReaderExtensions.cs create mode 100644 src/Neo/Extensions/SpanExtensions.cs delete mode 100644 src/Neo/IO/Helper.cs diff --git a/src/Neo/Cryptography/Helper.cs b/src/Neo/Cryptography/Helper.cs index f398612e61..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; 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/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/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/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/Payloads/Conditions/CalledByContractCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs index 4ce1e2dc1f..9cccec95cc 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByContractCondition.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; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs index ac233a156a..d4a3b74800 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/CalledByGroupCondition.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs index 18e94fbc40..6e76309432 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/GroupCondition.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/NotCondition.cs index fd813c7230..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; diff --git a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs index a9cf1de0e9..cde011c116 100644 --- a/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.cs +++ b/src/Neo/Network/P2P/Payloads/Conditions/ScriptHashCondition.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; 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/GetBlocksPayload.cs b/src/Neo/Network/P2P/Payloads/GetBlocksPayload.cs index 254287135f..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; 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/WitnessRule.cs b/src/Neo/Network/P2P/Payloads/WitnessRule.cs index 515d234666..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; 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/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/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/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/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/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/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/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/VM/Helper.cs b/src/Neo/VM/Helper.cs index fd95467da1..0a9a106e1f 100644 --- a/src/Neo/VM/Helper.cs +++ b/src/Neo/VM/Helper.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Cryptography.ECC; +using Neo.Extensions; using Neo.IO; using Neo.Json; using Neo.SmartContract; 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/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/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/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/SQLiteWallet/SQLiteWallet.cs b/src/Plugins/SQLiteWallet/SQLiteWallet.cs index 7b270ec81b..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; 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/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/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/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.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.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.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 5f2ad3c18f..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; diff --git a/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs b/tests/Neo.Plugins.RpcServer.Tests/UT_RpcServer.Wallet.cs index 801938903a..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; 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/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/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 5551daae65..b35d92f4e3 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_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/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/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/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_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_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.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..ae446503e6 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/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; From 6e000529d7efb0a7fd589e162a03a88cef799415 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 25 Nov 2024 22:08:22 -0500 Subject: [PATCH 53/53] Fixed `GetVarSize` (#3594) * Fixed `GetVarSize` * Added unit tests * revert `global.json` * Fixed test for `old` method * revert `global.json` * Update src/Neo.Extensions/UnsafeData.cs --------- Co-authored-by: Shargon --- src/Neo.Extensions/UnsafeData.cs | 8 +++--- tests/Neo.Extensions.Tests/UT_UnsafeData.cs | 29 ++++++++++++++++++--- 2 files changed, 30 insertions(+), 7 deletions(-) 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/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); + } } }