From ff58726405f128adf5c6f3e3ac7947f79ae78c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=BF=97=E5=90=8C?= Date: Thu, 12 Sep 2024 23:28:45 +0800 Subject: [PATCH 1/7] Fixing errors in comments (#3483) * Update OpCode.cs * Update JumpTable.Compound.cs * Update OpCode.cs --- src/Neo.VM/JumpTable/JumpTable.Compound.cs | 2 +- src/Neo.VM/OpCode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Neo.VM/JumpTable/JumpTable.Compound.cs b/src/Neo.VM/JumpTable/JumpTable.Compound.cs index 197cc1952a..817b460cf9 100644 --- a/src/Neo.VM/JumpTable/JumpTable.Compound.cs +++ b/src/Neo.VM/JumpTable/JumpTable.Compound.cs @@ -540,7 +540,7 @@ public virtual void ClearItems(ExecutionEngine engine, Instruction instruction) /// /// The execution engine. /// The instruction being executed. - /// Pop 1, Push 0 + /// Pop 1, Push 1 [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void PopItem(ExecutionEngine engine, Instruction instruction) { diff --git a/src/Neo.VM/OpCode.cs b/src/Neo.VM/OpCode.cs index 8ef5c0e538..6af023cb6a 100644 --- a/src/Neo.VM/OpCode.cs +++ b/src/Neo.VM/OpCode.cs @@ -2028,7 +2028,7 @@ public enum OpCode : byte /// Using this opcode will need to dup the array before using it. /// /// - /// Push: 0 item(s) + /// Push: 1 item(s) /// Pop: 1 item(s) /// /// From c6282cd3c84d2908b634a54af84e9d88f42491e4 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Sat, 14 Sep 2024 01:16:03 +0800 Subject: [PATCH 2/7] parse nef file scripts (#3482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * parse nef file scripts * nef file path support --------- Co-authored-by: Vitor Nazário Coelho --- src/Neo.CLI/CLI/MainService.Tools.cs | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Neo.CLI/CLI/MainService.Tools.cs b/src/Neo.CLI/CLI/MainService.Tools.cs index 60de31493a..c000b48655 100644 --- a/src/Neo.CLI/CLI/MainService.Tools.cs +++ b/src/Neo.CLI/CLI/MainService.Tools.cs @@ -14,10 +14,12 @@ using Neo.Extensions; using Neo.IO; using Neo.SmartContract; +using Neo.VM; using Neo.Wallets; using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Numerics; using System.Reflection; @@ -441,6 +443,58 @@ private static string Base64Fixed(string str) } } + /// + /// Base64 .nef file Analysis + /// + [ParseFunction("Base64 .nef file Analysis")] + private string? NefFileAnalyis(string base64) + { + byte[] nefData; + if (File.Exists(base64)) // extension name not considered + nefData = File.ReadAllBytes(base64); + else + { + try + { + nefData = Convert.FromBase64String(base64); + } + catch { return null; } + } + NefFile nef; + Script script; + bool verifyChecksum = false; + bool strictMode = false; + try + { + nef = NefFile.Parse(nefData, true); + verifyChecksum = true; + } + catch (FormatException) + { + nef = NefFile.Parse(nefData, false); + } + catch { return null; } + try + { + script = new Script(nef.Script, true); + strictMode = true; + } + catch (BadScriptException) + { + script = new Script(nef.Script, false); + } + catch { return null; } + string? result = ScriptsToOpCode(Convert.ToBase64String(nef.Script.ToArray())); + if (result == null) + return null; + string prefix = $"\r\n# Compiler: {nef.Compiler}"; + if (!verifyChecksum) + prefix += $"\r\n# Warning: Invalid .nef file checksum"; + if (!strictMode) + prefix += $"\r\n# Warning: Failed in {nameof(strictMode)}"; + return prefix + result; + } + /// /// Checks if the string is null or cannot be printed. /// From 994645054aa06a67b1c0ee80618c4f5a5edd1569 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 14 Sep 2024 17:07:29 +0800 Subject: [PATCH 3/7] [Neo VM Style] Throw exception for Integer that is larger than 32 bytes (#3486) * fix push integer * Update src/Neo.VM/ScriptBuilder.cs * Update tests/Neo.VM.Tests/UT_ScriptBuilder.cs * Update tests/Neo.VM.Tests/UT_ScriptBuilder.cs --- src/Neo.VM/ScriptBuilder.cs | 3 +- tests/Neo.VM.Tests/UT_ScriptBuilder.cs | 78 +++++++++++++++++--------- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/Neo.VM/ScriptBuilder.cs b/src/Neo.VM/ScriptBuilder.cs index 3b2f83171a..5615f6b918 100644 --- a/src/Neo.VM/ScriptBuilder.cs +++ b/src/Neo.VM/ScriptBuilder.cs @@ -104,7 +104,8 @@ public ScriptBuilder EmitPush(BigInteger value) <= 4 => Emit(OpCode.PUSHINT32, PadRight(buffer, bytesWritten, 4, value.Sign < 0)), <= 8 => Emit(OpCode.PUSHINT64, PadRight(buffer, bytesWritten, 8, value.Sign < 0)), <= 16 => Emit(OpCode.PUSHINT128, PadRight(buffer, bytesWritten, 16, value.Sign < 0)), - _ => Emit(OpCode.PUSHINT256, PadRight(buffer, bytesWritten, 32, value.Sign < 0)), + <= 32 => Emit(OpCode.PUSHINT256, PadRight(buffer, bytesWritten, 32, value.Sign < 0)), + _ => throw new ArgumentOutOfRangeException(nameof(value), "Invalid value: BigInteger is too large"), }; } diff --git a/tests/Neo.VM.Tests/UT_ScriptBuilder.cs b/tests/Neo.VM.Tests/UT_ScriptBuilder.cs index faa57f3345..6d5b4e6f2d 100644 --- a/tests/Neo.VM.Tests/UT_ScriptBuilder.cs +++ b/tests/Neo.VM.Tests/UT_ScriptBuilder.cs @@ -143,41 +143,63 @@ public void TestEmitJump() [TestMethod] public void TestEmitPushBigInteger() { - using (ScriptBuilder script = new()) + // Test small integers (-1 to 16) + for (var i = -1; i <= 16; i++) { - script.EmitPush(BigInteger.MinusOne); - CollectionAssert.AreEqual(new byte[] { 0x0F }, script.ToArray()); + using ScriptBuilder script = new(); + script.EmitPush(new BigInteger(i)); + CollectionAssert.AreEqual(new[] { (byte)(OpCode.PUSH0 + (byte)i) }, script.ToArray()); } - using (ScriptBuilder script = new()) - { - script.EmitPush(BigInteger.Zero); - CollectionAssert.AreEqual(new byte[] { 0x10 }, script.ToArray()); - } + // Test -1 + Assert.AreEqual("0x0f", new ScriptBuilder().EmitPush(BigInteger.MinusOne).ToArray().ToHexString()); - for (byte x = 1; x <= 16; x++) - { - using ScriptBuilder script = new(); - script.EmitPush(new BigInteger(x)); - CollectionAssert.AreEqual(new byte[] { (byte)(OpCode.PUSH0 + x) }, script.ToArray()); - } + // Test edge cases for different sizes + // PUSHINT8 + Assert.AreEqual("0x0080", new ScriptBuilder().EmitPush(sbyte.MinValue).ToArray().ToHexString()); + Assert.AreEqual("0x007f", new ScriptBuilder().EmitPush(sbyte.MaxValue).ToArray().ToHexString()); + + // PUSHINT16 + Assert.AreEqual("0x010080", new ScriptBuilder().EmitPush(short.MinValue).ToArray().ToHexString()); + Assert.AreEqual("0x01ff7f", new ScriptBuilder().EmitPush(short.MaxValue).ToArray().ToHexString()); + + // PUSHINT32 + Assert.AreEqual("0x0200000080", new ScriptBuilder().EmitPush(int.MinValue).ToArray().ToHexString()); + Assert.AreEqual("0x02ffffff7f", new ScriptBuilder().EmitPush(int.MaxValue).ToArray().ToHexString()); + + // PUSHINT64 + Assert.AreEqual("0x030000000000000080", new ScriptBuilder().EmitPush(long.MinValue).ToArray().ToHexString()); + Assert.AreEqual("0x03ffffffffffffff7f", new ScriptBuilder().EmitPush(long.MaxValue).ToArray().ToHexString()); + + // PUSHINT128 + Assert.AreEqual("0x04ffffffffffffffff0000000000000000", new ScriptBuilder().EmitPush(new BigInteger(ulong.MaxValue)).ToArray().ToHexString()); + Assert.AreEqual("0x0400000000000000000100000000000000", new ScriptBuilder().EmitPush(new BigInteger(ulong.MaxValue) + 1).ToArray().ToHexString()); + + // PUSHINT256, case from https://en.wikipedia.org/wiki/256-bit_computing#:~:text=The%20range%20of%20a%20signed,%2C%E2%80%8B819%2C%E2%80%8B967. + Assert.AreEqual("0x050000000000000000000000000000000000000000000000000000000000000080", + new ScriptBuilder().EmitPush(BigInteger.Parse("-57896044618658097711785492504343953926634992332820282019728792003956564819968")).ToArray().ToHexString()); + + Assert.AreEqual("0x05ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + new ScriptBuilder().EmitPush(BigInteger.Parse("57896044618658097711785492504343953926634992332820282019728792003956564819967")).ToArray().ToHexString()); + + // Test exceeding 256-bit value (2^256) + Assert.ThrowsException(() => + new ScriptBuilder().EmitPush(BigInteger.Parse("115792089237316195423570985008687907853269984665640564039457584007913129639936"))); + + // Test negative numbers + Assert.AreEqual("0x00fe", new ScriptBuilder().EmitPush(new BigInteger(-2)).ToArray().ToHexString()); + Assert.AreEqual("0x0100ff", new ScriptBuilder().EmitPush(new BigInteger(-256)).ToArray().ToHexString()); + + // Test numbers that are exactly at the boundary + Assert.AreEqual("0x04ffffffffffffffff0000000000000000", new ScriptBuilder().EmitPush(BigInteger.Parse("18446744073709551615")).ToArray().ToHexString()); + Assert.AreEqual("0x0400000000000000000100000000000000", new ScriptBuilder().EmitPush(BigInteger.Parse("18446744073709551616")).ToArray().ToHexString()); - CollectionAssert.AreEqual("0080".FromHexString(), new ScriptBuilder().EmitPush(sbyte.MinValue).ToArray()); - CollectionAssert.AreEqual("007f".FromHexString(), new ScriptBuilder().EmitPush(sbyte.MaxValue).ToArray()); - CollectionAssert.AreEqual("01ff00".FromHexString(), new ScriptBuilder().EmitPush(byte.MaxValue).ToArray()); - CollectionAssert.AreEqual("010080".FromHexString(), new ScriptBuilder().EmitPush(short.MinValue).ToArray()); - CollectionAssert.AreEqual("01ff7f".FromHexString(), new ScriptBuilder().EmitPush(short.MaxValue).ToArray()); - CollectionAssert.AreEqual("02ffff0000".FromHexString(), new ScriptBuilder().EmitPush(ushort.MaxValue).ToArray()); - CollectionAssert.AreEqual("0200000080".FromHexString(), new ScriptBuilder().EmitPush(int.MinValue).ToArray()); - CollectionAssert.AreEqual("02ffffff7f".FromHexString(), new ScriptBuilder().EmitPush(int.MaxValue).ToArray()); - CollectionAssert.AreEqual("03ffffffff00000000".FromHexString(), new ScriptBuilder().EmitPush(uint.MaxValue).ToArray()); - CollectionAssert.AreEqual("030000000000000080".FromHexString(), new ScriptBuilder().EmitPush(long.MinValue).ToArray()); - CollectionAssert.AreEqual("03ffffffffffffff7f".FromHexString(), new ScriptBuilder().EmitPush(long.MaxValue).ToArray()); - CollectionAssert.AreEqual("04ffffffffffffffff0000000000000000".FromHexString(), new ScriptBuilder().EmitPush(ulong.MaxValue).ToArray()); - CollectionAssert.AreEqual("050100000000000000feffffffffffffff00000000000000000000000000000000".FromHexString(), new ScriptBuilder().EmitPush(new BigInteger(ulong.MaxValue) * new BigInteger(ulong.MaxValue)).ToArray()); + // Test very large negative number + Assert.AreEqual("0x040000000000000000ffffffffffffffff", new ScriptBuilder().EmitPush(BigInteger.Parse("-18446744073709551616")).ToArray().ToHexString()); + // Test exception for too large BigInteger Assert.ThrowsException(() => new ScriptBuilder().EmitPush( - new BigInteger("050100000000000000feffffffffffffff0100000000000000feffffffffffffff00000000000000000000000000000000".FromHexString()))); + BigInteger.Parse("115792089237316195423570985008687907853269984665640564039457584007913129639937"))); } [TestMethod] From a52e3214787faa0c91027037ae1a0645b9731bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=BF=97=E5=90=8C?= Date: Tue, 24 Sep 2024 18:13:39 +0800 Subject: [PATCH 4/7] Update RpcError.cs (#3498) --- src/Plugins/RpcServer/RpcError.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/RpcServer/RpcError.cs b/src/Plugins/RpcServer/RpcError.cs index 667a3906d4..7f383025a5 100644 --- a/src/Plugins/RpcServer/RpcError.cs +++ b/src/Plugins/RpcServer/RpcError.cs @@ -55,7 +55,7 @@ public class RpcError public static readonly RpcError AlreadyInPool = new(-503, "Already in pool"); public static readonly RpcError InsufficientNetworkFee = new(-504, "Insufficient network fee"); public static readonly RpcError PolicyFailed = new(-505, "Policy check failed"); - public static readonly RpcError InvalidScript = new(-509, "Invalid transaction script"); + public static readonly RpcError InvalidScript = new(-506, "Invalid transaction script"); public static readonly RpcError InvalidAttribute = new(-507, "Invalid transaction attribute"); public static readonly RpcError InvalidSignature = new(-508, "Invalid signature"); public static readonly RpcError InvalidSize = new(-509, "Invalid inventory size"); From d6ed5a5e927b384fb23e4c2459fc1f27bbbd04ea Mon Sep 17 00:00:00 2001 From: nan01ab Date: Fri, 27 Sep 2024 05:57:22 +0800 Subject: [PATCH 5/7] fix: concurrency conflict in HeaderCache.Count (#3501) * fix: concurrency conflict in HeaderCache.Count * Update tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs * Update tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs --------- Co-authored-by: Shargon --- src/Neo/Ledger/HeaderCache.cs | 18 ++++- tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs | 70 ++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs diff --git a/src/Neo/Ledger/HeaderCache.cs b/src/Neo/Ledger/HeaderCache.cs index ac2208011f..6cc8acf6a7 100644 --- a/src/Neo/Ledger/HeaderCache.cs +++ b/src/Neo/Ledger/HeaderCache.cs @@ -55,12 +55,26 @@ public Header this[uint index] /// /// Gets the number of elements in the cache. /// - public int Count => headers.Count; + public int Count + { + get + { + readerWriterLock.EnterReadLock(); + try + { + return headers.Count; + } + finally + { + readerWriterLock.ExitReadLock(); + } + } + } /// /// Indicates whether the cache is full. /// - public bool Full => headers.Count >= 10000; + public bool Full => Count >= 10000; /// /// Gets the last in the cache. Or if the cache is empty. diff --git a/tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs b/tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs new file mode 100644 index 0000000000..71706fda32 --- /dev/null +++ b/tests/Neo.UnitTests/Ledger/UT_HeaderCache.cs @@ -0,0 +1,70 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_HeaderCache.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 FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using System; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_HeaderCache + { + [TestMethod] + public void TestHeaderCache() + { + var cache = new HeaderCache(); + var header = new Header(); + header.Index = 1; + cache.Add(header); + + var got = cache[1]; + got.Should().NotBeNull(); + got.Index.Should().Be(1); + + var count = cache.Count; + count.Should().Be(1); + + var full = cache.Full; + full.Should().BeFalse(); + + var last = cache.Last; + last.Should().NotBeNull(); + last.Index.Should().Be(1); + + got = cache[2]; + got.Should().BeNull(); + + // enumerate + var enumerator = cache.GetEnumerator(); + enumerator.MoveNext().Should().BeTrue(); + enumerator.Current.Index.Should().Be(1); + enumerator.MoveNext().Should().BeFalse(); + + var removed = cache.TryRemoveFirst(out header); + removed.Should().BeTrue(); + + count = cache.Count; + count.Should().Be(0); + + full = cache.Full; + full.Should().BeFalse(); + + last = cache.Last; + last.Should().BeNull(); + + got = cache[1]; + got.Should().BeNull(); + } + } +} From 73cea0173d14b8c29dfb6f7d0b0792aff4c75d5b Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 26 Sep 2024 22:48:11 -0400 Subject: [PATCH 6/7] [`Add`] Transaction Builder (#3477) * Added Builders with tests * Added SignerBuilder and started WitnessRuleBuilder * Added `WitnessConditionBuilder` with tests * Added more logic * Fixed `SignerBuilder` class * Code touch ups * Added more tests * Update src/Neo/Builders/TransactionBuilder.cs * Fixed `And` `Or` and `Not` conditions * Fixed Memory leak * Added error message for Witness scripts --------- Co-authored-by: Jimmy Co-authored-by: Shargon Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- src/Neo/Builders/AndConditionBuilder.cs | 90 +++++++ src/Neo/Builders/OrConditionBuilder.cs | 90 +++++++ src/Neo/Builders/SignerBuilder.cs | 73 ++++++ .../Builders/TransactionAttributesBuilder.cs | 74 ++++++ src/Neo/Builders/TransactionBuilder.cs | 116 +++++++++ src/Neo/Builders/WitnessBuilder.cs | 79 +++++++ src/Neo/Builders/WitnessConditionBuilder.cs | 131 +++++++++++ src/Neo/Builders/WitnessRuleBuilder.cs | 44 ++++ .../Builders/UT_SignerBuilder.cs | 101 ++++++++ .../UT_TransactionAttributesBuilder.cs | 92 ++++++++ .../Builders/UT_TransactionBuilder.cs | 201 ++++++++++++++++ .../Builders/UT_WitnessBuilder.cs | 91 ++++++++ .../Builders/UT_WitnessConditionBuilder.cs | 220 ++++++++++++++++++ .../Builders/UT_WitnessRuleBuilder.cs | 69 ++++++ 14 files changed, 1471 insertions(+) create mode 100644 src/Neo/Builders/AndConditionBuilder.cs create mode 100644 src/Neo/Builders/OrConditionBuilder.cs create mode 100644 src/Neo/Builders/SignerBuilder.cs create mode 100644 src/Neo/Builders/TransactionAttributesBuilder.cs create mode 100644 src/Neo/Builders/TransactionBuilder.cs create mode 100644 src/Neo/Builders/WitnessBuilder.cs create mode 100644 src/Neo/Builders/WitnessConditionBuilder.cs create mode 100644 src/Neo/Builders/WitnessRuleBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_SignerBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_TransactionAttributesBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_WitnessBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs diff --git a/src/Neo/Builders/AndConditionBuilder.cs b/src/Neo/Builders/AndConditionBuilder.cs new file mode 100644 index 0000000000..a75a52227a --- /dev/null +++ b/src/Neo/Builders/AndConditionBuilder.cs @@ -0,0 +1,90 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// AndConditionBuilder.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads.Conditions; +using System; + +namespace Neo.Builders +{ + public sealed class AndConditionBuilder + { + private readonly AndCondition _condition = new() { Expressions = [] }; + + private AndConditionBuilder() { } + + public static AndConditionBuilder CreateEmpty() + { + return new AndConditionBuilder(); + } + + public AndConditionBuilder And(Action config) + { + var acb = new AndConditionBuilder(); + config(acb); + + _condition.Expressions = [.. _condition.Expressions, acb.Build()]; + + return this; + } + + public AndConditionBuilder Or(Action config) + { + var ocb = OrConditionBuilder.CreateEmpty(); + config(ocb); + + _condition.Expressions = [.. _condition.Expressions, ocb.Build()]; + + return this; + } + + public AndConditionBuilder Boolean(bool expression) + { + _condition.Expressions = [.. _condition.Expressions, new BooleanCondition { Expression = expression }]; + return this; + } + + public AndConditionBuilder CalledByContract(UInt160 hash) + { + _condition.Expressions = [.. _condition.Expressions, new CalledByContractCondition { Hash = hash }]; + return this; + } + + public AndConditionBuilder CalledByEntry() + { + _condition.Expressions = [.. _condition.Expressions, new CalledByEntryCondition()]; + return this; + } + + public AndConditionBuilder CalledByGroup(ECPoint publicKey) + { + _condition.Expressions = [.. _condition.Expressions, new CalledByGroupCondition { Group = publicKey }]; + return this; + } + + public AndConditionBuilder Group(ECPoint publicKey) + { + _condition.Expressions = [.. _condition.Expressions, new GroupCondition() { Group = publicKey }]; + return this; + } + + public AndConditionBuilder ScriptHash(UInt160 scriptHash) + { + _condition.Expressions = [.. _condition.Expressions, new ScriptHashCondition() { Hash = scriptHash }]; + return this; + } + + public AndCondition Build() + { + return _condition; + } + } +} diff --git a/src/Neo/Builders/OrConditionBuilder.cs b/src/Neo/Builders/OrConditionBuilder.cs new file mode 100644 index 0000000000..6b8ca2a0e1 --- /dev/null +++ b/src/Neo/Builders/OrConditionBuilder.cs @@ -0,0 +1,90 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OrConditionBuilder.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads.Conditions; +using System; + +namespace Neo.Builders +{ + public sealed class OrConditionBuilder + { + private readonly OrCondition _condition = new() { Expressions = [] }; + + private OrConditionBuilder() { } + + public static OrConditionBuilder CreateEmpty() + { + return new OrConditionBuilder(); + } + + public OrConditionBuilder And(Action config) + { + var acb = AndConditionBuilder.CreateEmpty(); + config(acb); + + _condition.Expressions = [.. _condition.Expressions, acb.Build()]; + + return this; + } + + public OrConditionBuilder Or(Action config) + { + var acb = new OrConditionBuilder(); + config(acb); + + _condition.Expressions = [.. _condition.Expressions, acb.Build()]; + + return this; + } + + public OrConditionBuilder Boolean(bool expression) + { + _condition.Expressions = [.. _condition.Expressions, new BooleanCondition { Expression = expression }]; + return this; + } + + public OrConditionBuilder CalledByContract(UInt160 hash) + { + _condition.Expressions = [.. _condition.Expressions, new CalledByContractCondition { Hash = hash }]; + return this; + } + + public OrConditionBuilder CalledByEntry() + { + _condition.Expressions = [.. _condition.Expressions, new CalledByEntryCondition()]; + return this; + } + + public OrConditionBuilder CalledByGroup(ECPoint publicKey) + { + _condition.Expressions = [.. _condition.Expressions, new CalledByGroupCondition { Group = publicKey }]; + return this; + } + + public OrConditionBuilder Group(ECPoint publicKey) + { + _condition.Expressions = [.. _condition.Expressions, new GroupCondition() { Group = publicKey }]; + return this; + } + + public OrConditionBuilder ScriptHash(UInt160 scriptHash) + { + _condition.Expressions = [.. _condition.Expressions, new ScriptHashCondition() { Hash = scriptHash }]; + return this; + } + + public OrCondition Build() + { + return _condition; + } + } +} diff --git a/src/Neo/Builders/SignerBuilder.cs b/src/Neo/Builders/SignerBuilder.cs new file mode 100644 index 0000000000..76b70f1efe --- /dev/null +++ b/src/Neo/Builders/SignerBuilder.cs @@ -0,0 +1,73 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SignerBuilder.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using System; + +namespace Neo.Builders +{ + public sealed class SignerBuilder + { + private readonly Signer _signer = new Signer() + { + Account = UInt160.Zero, + AllowedContracts = [], + AllowedGroups = [], + Rules = [], + Scopes = WitnessScope.None, + }; + + private SignerBuilder() { } + + public static SignerBuilder CreateEmpty() + { + return new SignerBuilder(); + } + + public SignerBuilder Account(UInt160 scriptHash) + { + _signer.Account = scriptHash; + return this; + } + + public SignerBuilder AllowContract(UInt160 contractHash) + { + _signer.AllowedContracts = [.. _signer.AllowedContracts, contractHash]; + return this; + } + + public SignerBuilder AllowGroup(ECPoint publicKey) + { + _signer.AllowedGroups = [.. _signer.AllowedGroups, publicKey]; + return this; + } + + public SignerBuilder AddWitnessScope(WitnessScope scope) + { + _signer.Scopes |= scope; + return this; + } + + public SignerBuilder AddWitnessRule(WitnessRuleAction action, Action config) + { + var rb = WitnessRuleBuilder.Create(action); + config(rb); + _signer.Rules = [.. _signer.Rules, rb.Build()]; + return this; + } + + public Signer Build() + { + return _signer; + } + } +} diff --git a/src/Neo/Builders/TransactionAttributesBuilder.cs b/src/Neo/Builders/TransactionAttributesBuilder.cs new file mode 100644 index 0000000000..9eb4ae99e3 --- /dev/null +++ b/src/Neo/Builders/TransactionAttributesBuilder.cs @@ -0,0 +1,74 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionAttributesBuilder.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.Network.P2P.Payloads; +using System; +using System.Linq; + +namespace Neo.Builders +{ + public sealed class TransactionAttributesBuilder + { + private TransactionAttribute[] _attributes = []; + + private TransactionAttributesBuilder() { } + + public static TransactionAttributesBuilder CreateEmpty() + { + return new TransactionAttributesBuilder(); + } + + public TransactionAttributesBuilder AddConflict(Action config) + { + var conflicts = new Conflicts(); + config(conflicts); + _attributes = [.. _attributes, conflicts]; + return this; + } + + public TransactionAttributesBuilder AddOracleResponse(Action config) + { + var oracleResponse = new OracleResponse(); + config(oracleResponse); + _attributes = [.. _attributes, oracleResponse]; + return this; + } + + public TransactionAttributesBuilder AddHighPriority() + { + if (_attributes.Any(a => a is HighPriorityAttribute)) + throw new InvalidOperationException("HighPriority already exists in the attributes."); + + var highPriority = new HighPriorityAttribute(); + _attributes = [.. _attributes, highPriority]; + return this; + } + + public TransactionAttributesBuilder AddNotValidBefore(uint block) + { + if (_attributes.Any(a => a is NotValidBefore b && b.Height == block)) + throw new InvalidOperationException($"Block {block} already exists in the attributes."); + + var validUntilBlock = new NotValidBefore() + { + Height = block + }; + + _attributes = [.. _attributes, validUntilBlock]; + return this; + } + + public TransactionAttribute[] Build() + { + return _attributes; + } + } +} diff --git a/src/Neo/Builders/TransactionBuilder.cs b/src/Neo/Builders/TransactionBuilder.cs new file mode 100644 index 0000000000..2a1a7a9424 --- /dev/null +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -0,0 +1,116 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// TransactionBuilder.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.Network.P2P.Payloads; +using Neo.VM; +using System; + +namespace Neo.Builders +{ + public sealed class TransactionBuilder + { + private readonly Transaction _tx = new() + { + Script = new[] { (byte)OpCode.RET }, + Attributes = [], + Signers = [], + Witnesses = [], + }; + + private TransactionBuilder() { } + + public static TransactionBuilder CreateEmpty() + { + return new TransactionBuilder(); + } + + public TransactionBuilder Version(byte version) + { + _tx.Version = version; + return this; + } + + public TransactionBuilder Nonce(uint nonce) + { + _tx.Nonce = nonce; + return this; + } + + public TransactionBuilder SystemFee(uint systemFee) + { + _tx.SystemFee = systemFee; + return this; + } + + public TransactionBuilder NetworkFee(uint networkFee) + { + _tx.NetworkFee = networkFee; + return this; + } + + public TransactionBuilder ValidUntil(uint blockIndex) + { + _tx.ValidUntilBlock = blockIndex; + return this; + } + + public TransactionBuilder AttachSystem(Action config) + { + using var sb = new ScriptBuilder(); + config(sb); + _tx.Script = sb.ToArray(); + return this; + } + + public TransactionBuilder AttachSystem(byte[] script) + { + _tx.Script = script; + return this; + } + + public TransactionBuilder AddAttributes(Action config) + { + var ab = TransactionAttributesBuilder.CreateEmpty(); + config(ab); + _tx.Attributes = ab.Build(); + return this; + } + + public TransactionBuilder AddWitness(Action config) + { + var wb = WitnessBuilder.CreateEmpty(); + config(wb); + _tx.Witnesses = [.. _tx.Witnesses, wb.Build()]; + return this; + } + + public TransactionBuilder AddWitness(Action config) + { + var wb = WitnessBuilder.CreateEmpty(); + config(wb, _tx); + _tx.Witnesses = [.. _tx.Witnesses, wb.Build()]; + return this; + } + + public TransactionBuilder AddSigner(Action config) + { + var wb = SignerBuilder.CreateEmpty(); + config(wb, _tx); + _tx.Signers = [.. _tx.Signers, wb.Build()]; + return this; + } + + public Transaction Build() + { + return _tx; + } + } +} diff --git a/src/Neo/Builders/WitnessBuilder.cs b/src/Neo/Builders/WitnessBuilder.cs new file mode 100644 index 0000000000..e555195688 --- /dev/null +++ b/src/Neo/Builders/WitnessBuilder.cs @@ -0,0 +1,79 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessBuilder.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.Network.P2P.Payloads; +using Neo.VM; +using System; + +namespace Neo.Builders +{ + public sealed class WitnessBuilder + { + private byte[] _invocationScript = []; + private byte[] _verificationScript = []; + + private WitnessBuilder() { } + + public static WitnessBuilder CreateEmpty() + { + return new WitnessBuilder(); + } + + public WitnessBuilder AddInvocation(Action config) + { + if (_invocationScript.Length > 0) + throw new InvalidOperationException("Invocation script already exists."); + + using var sb = new ScriptBuilder(); + config(sb); + _invocationScript = sb.ToArray(); + return this; + } + + public WitnessBuilder AddInvocation(byte[] bytes) + { + if (_invocationScript.Length > 0) + throw new InvalidOperationException("Invocation script already exists."); + + _invocationScript = bytes; + return this; + } + + public WitnessBuilder AddVerification(Action config) + { + if (_verificationScript.Length > 0) + throw new InvalidOperationException("Verification script already exists."); + + using var sb = new ScriptBuilder(); + config(sb); + _verificationScript = sb.ToArray(); + return this; + } + + public WitnessBuilder AddVerification(byte[] bytes) + { + if (_verificationScript.Length > 0) + throw new InvalidOperationException("Verification script already exists."); + + _verificationScript = bytes; + return this; + } + + public Witness Build() + { + return new Witness() + { + InvocationScript = _invocationScript, + VerificationScript = _verificationScript, + }; + } + } +} diff --git a/src/Neo/Builders/WitnessConditionBuilder.cs b/src/Neo/Builders/WitnessConditionBuilder.cs new file mode 100644 index 0000000000..41cfc05163 --- /dev/null +++ b/src/Neo/Builders/WitnessConditionBuilder.cs @@ -0,0 +1,131 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessConditionBuilder.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads.Conditions; +using System; + +namespace Neo.Builders +{ + public sealed class WitnessConditionBuilder + { + WitnessCondition _condition; + + private WitnessConditionBuilder() { } + + private WitnessConditionBuilder(WitnessCondition condition) + { + _condition = condition; + } + + public static WitnessConditionBuilder Create() + { + return new WitnessConditionBuilder(); + } + + public WitnessConditionBuilder And(Action config) + { + var acb = AndConditionBuilder.CreateEmpty(); + config(acb); + + _condition = acb.Build(); + + return this; + } + + public WitnessConditionBuilder Boolean(bool expression) + { + var condition = new BooleanCondition() { Expression = expression }; + + _condition = condition; + + return this; + } + + public WitnessConditionBuilder CalledByContract(UInt160 hash) + { + var condition = new CalledByContractCondition() { Hash = hash }; + + _condition = condition; + + return this; + } + + public WitnessConditionBuilder CalledByEntry() + { + var condition = new CalledByEntryCondition(); + + _condition = condition; + + return this; + } + + public WitnessConditionBuilder CalledByGroup(ECPoint publicKey) + { + var condition = new CalledByGroupCondition() { Group = publicKey }; + + _condition = condition; + + return this; + } + + public WitnessConditionBuilder Group(ECPoint publicKey) + { + var condition = new GroupCondition() { Group = publicKey }; + + _condition = condition; + + return this; + } + + public WitnessConditionBuilder Not(Action config) + { + var wcb = new WitnessConditionBuilder(); + config(wcb); + + var condition = new NotCondition() + { + Expression = wcb.Build() + }; + + _condition = condition; + + return this; + } + + public WitnessConditionBuilder Or(Action config) + { + var ocb = OrConditionBuilder.CreateEmpty(); + config(ocb); + + _condition = ocb.Build(); + + return this; + } + + public WitnessConditionBuilder ScriptHash(UInt160 scriptHash) + { + var condition = new ScriptHashCondition() { Hash = scriptHash }; + + _condition = condition; + + return this; + } + + public WitnessCondition Build() + { + if (_condition is null) + return new BooleanCondition() { Expression = true }; + + return _condition; + } + } +} diff --git a/src/Neo/Builders/WitnessRuleBuilder.cs b/src/Neo/Builders/WitnessRuleBuilder.cs new file mode 100644 index 0000000000..7c05a524d6 --- /dev/null +++ b/src/Neo/Builders/WitnessRuleBuilder.cs @@ -0,0 +1,44 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// WitnessRuleBuilder.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.Network.P2P.Payloads; +using System; + +namespace Neo.Builders +{ + public sealed class WitnessRuleBuilder + { + private readonly WitnessRule _rule = new(); + + private WitnessRuleBuilder(WitnessRuleAction action) + { + _rule.Action = action; + } + + public static WitnessRuleBuilder Create(WitnessRuleAction action) + { + return new WitnessRuleBuilder(action); + } + + public WitnessRuleBuilder AddCondition(Action config) + { + var cb = WitnessConditionBuilder.Create(); + config(cb); + _rule.Condition = cb.Build(); + return this; + } + + public WitnessRule Build() + { + return _rule; + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_SignerBuilder.cs b/tests/Neo.UnitTests/Builders/UT_SignerBuilder.cs new file mode 100644 index 0000000000..7d79f7428c --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_SignerBuilder.cs @@ -0,0 +1,101 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_SignerBuilder.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.Builders; +using Neo.Cryptography.ECC; +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.Builders +{ + [TestClass] + public class UT_SignerBuilder + { + [TestMethod] + public void TestCreateEmpty() + { + var sb = SignerBuilder.CreateEmpty(); + Assert.IsNotNull(sb); + } + + [TestMethod] + public void TestAccount() + { + var signer = SignerBuilder.CreateEmpty() + .Account(UInt160.Zero) + .Build(); + + Assert.IsNotNull(signer); + Assert.AreEqual(UInt160.Zero, signer.Account); + } + + [TestMethod] + public void TestAllowContract() + { + var signer = SignerBuilder.CreateEmpty() + .AllowContract(UInt160.Zero) + .Build(); + + Assert.IsNotNull(signer); + Assert.AreEqual(1, signer.AllowedContracts.Length); + Assert.AreEqual(UInt160.Zero, signer.AllowedContracts[0]); + } + + [TestMethod] + public void TestAllowGroup() + { + var myPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var signer = SignerBuilder.CreateEmpty() + .AllowGroup(myPublicKey) + .Build(); + + Assert.IsNotNull(signer); + Assert.AreEqual(1, signer.AllowedGroups.Length); + Assert.AreEqual(myPublicKey, signer.AllowedGroups[0]); + } + + [TestMethod] + public void TestAddWitnessScope() + { + var signer = SignerBuilder.CreateEmpty() + .AddWitnessScope(WitnessScope.Global) + .Build(); + + Assert.IsNotNull(signer); + Assert.AreEqual(WitnessScope.Global, signer.Scopes); + } + + [TestMethod] + public void TestAddWitnessRule() + { + var signer = SignerBuilder.CreateEmpty() + .AddWitnessRule(WitnessRuleAction.Allow, rb => + { + rb.AddCondition(cb => + { + cb.ScriptHash(UInt160.Zero); + }); + }) + .Build(); + + Assert.IsNotNull(signer); + Assert.AreEqual(1, signer.Rules.Length); + Assert.AreEqual(WitnessRuleAction.Allow, signer.Rules[0].Action); + Assert.IsInstanceOfType(signer.Rules[0].Condition); + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_TransactionAttributesBuilder.cs b/tests/Neo.UnitTests/Builders/UT_TransactionAttributesBuilder.cs new file mode 100644 index 0000000000..d9ade210ea --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_TransactionAttributesBuilder.cs @@ -0,0 +1,92 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TransactionAttributesBuilder.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.Builders; +using Neo.Network.P2P.Payloads; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Builders +{ + [TestClass] + public class UT_TransactionAttributesBuilder + { + [TestMethod] + public void TestCreateEmpty() + { + var builder = TransactionAttributesBuilder.CreateEmpty(); + + Assert.IsNotNull(builder); + } + + [TestMethod] + public void TestConflict() + { + var attr = TransactionAttributesBuilder.CreateEmpty() + .AddConflict(c => c.Hash = UInt256.Zero) + .Build(); + + Assert.IsNotNull(attr); + Assert.AreEqual(1, attr.Length); + Assert.IsInstanceOfType(attr[0]); + Assert.AreEqual(UInt256.Zero, ((Conflicts)attr[0]).Hash); + } + + [TestMethod] + public void TestOracleResponse() + { + var attr = TransactionAttributesBuilder.CreateEmpty() + .AddOracleResponse(c => + { + c.Id = 1ul; + c.Code = OracleResponseCode.Success; + c.Result = new byte[] { 0x01, 0x02, 0x03 }; + }) + .Build(); + + Assert.IsNotNull(attr); + Assert.AreEqual(1, attr.Length); + Assert.IsInstanceOfType(attr[0]); + Assert.AreEqual(1ul, ((OracleResponse)attr[0]).Id); + Assert.AreEqual(OracleResponseCode.Success, ((OracleResponse)attr[0]).Code); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, ((OracleResponse)attr[0]).Result.ToArray()); + } + + [TestMethod] + public void TestHighPriority() + { + var attr = TransactionAttributesBuilder.CreateEmpty() + .AddHighPriority() + .Build(); + + Assert.IsNotNull(attr); + Assert.AreEqual(1, attr.Length); + Assert.IsInstanceOfType(attr[0]); + } + + [TestMethod] + public void TestNotValidBefore() + { + var attr = TransactionAttributesBuilder.CreateEmpty() + .AddNotValidBefore(10u) + .Build(); + + Assert.IsNotNull(attr); + Assert.AreEqual(1, attr.Length); + Assert.IsInstanceOfType(attr[0]); + Assert.AreEqual(10u, ((NotValidBefore)attr[0]).Height); + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs new file mode 100644 index 0000000000..17eb5796ad --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs @@ -0,0 +1,201 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_TransactionBuilder.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.Builders; +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads.Conditions; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Builders +{ + [TestClass] + public class UT_TransactionBuilder + { + [TestMethod] + public void TestCreateEmpty() + { + var builder = TransactionBuilder.CreateEmpty(); + + Assert.IsNotNull(builder); + } + + [TestMethod] + public void TestEmptyTransaction() + { + var tx = TransactionBuilder.CreateEmpty() + .Build(); + + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestVersion() + { + byte expectedVersion = 1; + var tx = TransactionBuilder.CreateEmpty() + .Version(expectedVersion) + .Build(); + + Assert.AreEqual(expectedVersion, tx.Version); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestNonce() + { + var expectedNonce = (uint)Random.Shared.Next(); + var tx = TransactionBuilder.CreateEmpty() + .Nonce(expectedNonce) + .Build(); + + Assert.AreEqual(expectedNonce, tx.Nonce); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestSystemFee() + { + var expectedSystemFee = (uint)Random.Shared.Next(); + var tx = TransactionBuilder.CreateEmpty() + .SystemFee(expectedSystemFee) + .Build(); + + Assert.AreEqual(expectedSystemFee, tx.SystemFee); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestNetworkFee() + { + var expectedNetworkFee = (uint)Random.Shared.Next(); + var tx = TransactionBuilder.CreateEmpty() + .NetworkFee(expectedNetworkFee) + .Build(); + + Assert.AreEqual(expectedNetworkFee, tx.NetworkFee); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestValidUntilBlock() + { + var expectedValidUntilBlock = (uint)Random.Shared.Next(); + var tx = TransactionBuilder.CreateEmpty() + .ValidUntil(expectedValidUntilBlock) + .Build(); + + Assert.AreEqual(expectedValidUntilBlock, tx.ValidUntilBlock); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestAttachScript() + { + byte[] expectedScript = [(byte)OpCode.NOP]; + var tx = TransactionBuilder.CreateEmpty() + .AttachSystem(sb => sb.Emit(OpCode.NOP)) + .Build(); + + CollectionAssert.AreEqual(expectedScript, tx.Script.ToArray()); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestTransactionAttributes() + { + var tx = TransactionBuilder.CreateEmpty() + .AddAttributes(ab => ab.AddHighPriority()) + .Build(); + + Assert.AreEqual(1, tx.Attributes.Length); + Assert.IsInstanceOfType(tx.Attributes[0]); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestWitness() + { + var tx = TransactionBuilder.CreateEmpty() + .AddWitness(wb => + { + // Contract signature + wb.AddInvocation([]); + wb.AddVerification([]); + }) + .Build(); + + Assert.AreEqual(1, tx.Witnesses.Length); + Assert.AreEqual(0, tx.Witnesses[0].InvocationScript.Length); + Assert.AreEqual(0, tx.Witnesses[0].VerificationScript.Length); + Assert.IsNotNull(tx.Hash); + } + + [TestMethod] + public void TestWitnessWithTransactionParameter() + { + var tx = TransactionBuilder.CreateEmpty() + .AddWitness((wb, tx) => + { + // Checks to make sure the transaction is hash able + // NOTE: transaction can be used for signing here + Assert.IsNotNull(tx.Hash); + }) + .Build(); + } + + [TestMethod] + public void TestSigner() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var expectedContractHash = UInt160.Zero; + + var tx = TransactionBuilder.CreateEmpty() + .AddSigner((sb, tx) => + { + sb.Account(expectedContractHash); + sb.AllowContract(expectedContractHash); + sb.AllowGroup(expectedPublicKey); + sb.AddWitnessScope(WitnessScope.WitnessRules); + sb.AddWitnessRule(WitnessRuleAction.Deny, wrb => + { + wrb.AddCondition(cb => + { + cb.ScriptHash(expectedContractHash); + }); + }); + }) + .Build(); + + Assert.IsNotNull(tx.Hash); + Assert.AreEqual(1, tx.Signers.Length); + Assert.AreEqual(expectedContractHash, tx.Signers[0].Account); + Assert.AreEqual(1, tx.Signers[0].AllowedContracts.Length); + Assert.AreEqual(expectedContractHash, tx.Signers[0].AllowedContracts[0]); + Assert.AreEqual(1, tx.Signers[0].AllowedGroups.Length); + Assert.AreEqual(expectedPublicKey, tx.Signers[0].AllowedGroups[0]); + Assert.AreEqual(WitnessScope.WitnessRules, tx.Signers[0].Scopes); + Assert.AreEqual(1, tx.Signers[0].Rules.Length); + Assert.AreEqual(WitnessRuleAction.Deny, tx.Signers[0].Rules[0].Action); + Assert.IsNotNull(tx.Signers[0].Rules[0].Condition); + Assert.IsInstanceOfType(tx.Signers[0].Rules[0].Condition); + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_WitnessBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessBuilder.cs new file mode 100644 index 0000000000..1b73a69124 --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_WitnessBuilder.cs @@ -0,0 +1,91 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WitnessBuilder.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.Builders; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Builders +{ + [TestClass] + public class UT_WitnessBuilder + { + [TestMethod] + public void TestCreateEmpty() + { + var wb = WitnessBuilder.CreateEmpty(); + Assert.IsNotNull(wb); + } + + [TestMethod] + public void TestAddInvocationWithScriptBuilder() + { + var witness = WitnessBuilder.CreateEmpty() + .AddInvocation(sb => + { + sb.Emit(VM.OpCode.NOP); + sb.Emit(VM.OpCode.NOP); + sb.Emit(VM.OpCode.NOP); + }) + .Build(); + + Assert.IsNotNull(witness); + Assert.AreEqual(3, witness.InvocationScript.Length); + CollectionAssert.AreEqual(new byte[] { 0x21, 0x21, 0x21 }, witness.InvocationScript.ToArray()); + } + + [TestMethod] + public void TestAddInvocation() + { + var witness = WitnessBuilder.CreateEmpty() + .AddInvocation(new byte[] { 0x01, 0x02, 0x03 }) + .Build(); + + Assert.IsNotNull(witness); + Assert.AreEqual(3, witness.InvocationScript.Length); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, witness.InvocationScript.ToArray()); + } + + [TestMethod] + public void TestAddVerificationWithScriptBuilder() + { + var witness = WitnessBuilder.CreateEmpty() + .AddVerification(sb => + { + sb.Emit(VM.OpCode.NOP); + sb.Emit(VM.OpCode.NOP); + sb.Emit(VM.OpCode.NOP); + }) + .Build(); + + Assert.IsNotNull(witness); + Assert.AreEqual(3, witness.VerificationScript.Length); + CollectionAssert.AreEqual(new byte[] { 0x21, 0x21, 0x21 }, witness.VerificationScript.ToArray()); + } + + [TestMethod] + public void TestAddVerification() + { + var witness = WitnessBuilder.CreateEmpty() + .AddVerification(new byte[] { 0x01, 0x02, 0x03 }) + .Build(); + + Assert.IsNotNull(witness); + Assert.AreEqual(3, witness.VerificationScript.Length); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, witness.VerificationScript.ToArray()); + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs new file mode 100644 index 0000000000..4378aa2bfa --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs @@ -0,0 +1,220 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WitnessConditionBuilder.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.Builders; +using Neo.Cryptography.ECC; +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.Builders +{ + [TestClass] + public class UT_WitnessConditionBuilder + { + [TestMethod] + public void TestAndCondition() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var expectedContractHash = UInt160.Zero; + var condition = WitnessConditionBuilder.Create() + .And(and => + { + and.CalledByContract(expectedContractHash); + and.CalledByGroup(expectedPublicKey); + }) + .Build(); + + var actual = condition as AndCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.AreEqual(2, actual.Expressions.Length); + Assert.IsInstanceOfType(actual.Expressions[0]); + Assert.IsInstanceOfType(actual.Expressions[1]); + Assert.AreEqual(expectedContractHash, (actual.Expressions[0] as CalledByContractCondition).Hash); + Assert.AreEqual(expectedPublicKey, (actual.Expressions[1] as CalledByGroupCondition).Group); + } + + [TestMethod] + public void TestOrCondition() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var expectedContractHash = UInt160.Zero; + var condition = WitnessConditionBuilder.Create() + .Or(or => + { + or.CalledByContract(expectedContractHash); + or.CalledByGroup(expectedPublicKey); + }) + .Build(); + + var actual = condition as OrCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.AreEqual(2, actual.Expressions.Length); + Assert.IsInstanceOfType(actual.Expressions[0]); + Assert.IsInstanceOfType(actual.Expressions[1]); + Assert.AreEqual(expectedContractHash, (actual.Expressions[0] as CalledByContractCondition).Hash); + Assert.AreEqual(expectedPublicKey, (actual.Expressions[1] as CalledByGroupCondition).Group); + } + + [TestMethod] + public void TestBoolean() + { + var condition = WitnessConditionBuilder.Create() + .Boolean(true) + .Build(); + + var actual = condition as BooleanCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.IsTrue(actual.Expression); + } + + [TestMethod] + public void TestCalledByContract() + { + var expectedContractHash = UInt160.Zero; + var condition = WitnessConditionBuilder.Create() + .CalledByContract(expectedContractHash) + .Build(); + + var actual = condition as CalledByContractCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.AreEqual(expectedContractHash, actual.Hash); + } + + [TestMethod] + public void TestCalledByEntry() + { + var condition = WitnessConditionBuilder.Create() + .CalledByEntry() + .Build(); + + var actual = condition as CalledByEntryCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + } + + [TestMethod] + public void TestCalledByGroup() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var condition = WitnessConditionBuilder.Create() + .CalledByGroup(expectedPublicKey) + .Build(); + + var actual = condition as CalledByGroupCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.AreEqual(expectedPublicKey, actual.Group); + } + + [TestMethod] + public void TestGroup() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var condition = WitnessConditionBuilder.Create() + .Group(expectedPublicKey) + .Build(); + + var actual = condition as GroupCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.AreEqual(expectedPublicKey, actual.Group); + } + + [TestMethod] + public void TestScriptHash() + { + var expectedContractHash = UInt160.Zero; + var condition = WitnessConditionBuilder.Create() + .ScriptHash(expectedContractHash) + .Build(); + + var actual = condition as ScriptHashCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.AreEqual(expectedContractHash, actual.Hash); + } + + [TestMethod] + public void TestNotConditionWithAndCondition() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var expectedContractHash = UInt160.Zero; + var condition = WitnessConditionBuilder.Create() + .Not(not => + { + not.And(and => + { + and.CalledByContract(expectedContractHash); + and.CalledByGroup(expectedPublicKey); + }); + }) + .Build(); + + var actual = condition as NotCondition; + var actualAndCondition = actual.Expression as AndCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.IsInstanceOfType(actual.Expression); + Assert.AreEqual(2, actualAndCondition.Expressions.Length); + Assert.IsInstanceOfType(actualAndCondition.Expressions[0]); + Assert.IsInstanceOfType(actualAndCondition.Expressions[1]); + Assert.AreEqual(expectedContractHash, (actualAndCondition.Expressions[0] as CalledByContractCondition).Hash); + Assert.AreEqual(expectedPublicKey, (actualAndCondition.Expressions[1] as CalledByGroupCondition).Group); + } + + [TestMethod] + public void TestNotConditionWithOrCondition() + { + var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); + var expectedContractHash = UInt160.Zero; + var condition = WitnessConditionBuilder.Create() + .Not(not => + { + not.Or(or => + { + or.CalledByContract(expectedContractHash); + or.CalledByGroup(expectedPublicKey); + }); + }) + .Build(); + + var actual = condition as NotCondition; + var actualOrCondition = actual.Expression as OrCondition; + + Assert.IsNotNull(actual); + Assert.IsInstanceOfType(condition); + Assert.IsInstanceOfType(actual.Expression); + Assert.AreEqual(2, actualOrCondition.Expressions.Length); + Assert.IsInstanceOfType(actualOrCondition.Expressions[0]); + Assert.IsInstanceOfType(actualOrCondition.Expressions[1]); + Assert.AreEqual(expectedContractHash, (actualOrCondition.Expressions[0] as CalledByContractCondition).Hash); + Assert.AreEqual(expectedPublicKey, (actualOrCondition.Expressions[1] as CalledByGroupCondition).Group); + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs new file mode 100644 index 0000000000..1b87e2c52f --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs @@ -0,0 +1,69 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_WitnessRuleBuilder.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.Builders; +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.Builders +{ + [TestClass] + public class UT_WitnessRuleBuilder + { + [TestMethod] + public void TestCreate() + { + var builder = WitnessRuleBuilder.Create(WitnessRuleAction.Allow); + + Assert.IsNotNull(builder); + } + + [TestMethod] + public void TestCondition() + { + var rule = WitnessRuleBuilder.Create(WitnessRuleAction.Allow) + .AddCondition(wcb => + { + wcb.ScriptHash(UInt160.Zero); + }).Build(); + + Assert.IsNotNull(rule.Condition); + Assert.AreEqual(WitnessRuleAction.Allow, rule.Action); + Assert.IsInstanceOfType(rule.Condition); + Assert.AreEqual(UInt160.Zero, ((ScriptHashCondition)rule.Condition).Hash); + } + + [TestMethod] + public void TestCondition2() + { + var rule = WitnessRuleBuilder.Create(WitnessRuleAction.Allow) + .AddCondition(wcb => + { + wcb.And(and => + { + and.ScriptHash(UInt160.Zero); + }); + }).Build(); + + Assert.IsNotNull(rule.Condition); + Assert.AreEqual(WitnessRuleAction.Allow, rule.Action); + Assert.IsInstanceOfType(rule.Condition); + Assert.IsInstanceOfType((rule.Condition as AndCondition).Expressions[0]); + Assert.AreEqual(UInt160.Zero, ((rule.Condition as AndCondition).Expressions[0] as ScriptHashCondition).Hash); + } + } +} From 6a4ea1f7baef475705da72b666065d97bf12dce6 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 26 Sep 2024 22:57:07 -0400 Subject: [PATCH 7/7] [`Fix`] Neo Plugins github nuget (#3493) * Fixed Delete packages for github * Automatic * added continue-on-error: true --------- Co-authored-by: Shargon Co-authored-by: NGD Admin <154295625+NGDAdmin@users.noreply.github.com> --- .editorconfig | 5 ++ .github/workflows/pkgs-delete.yml | 91 +++++++++++-------------------- 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/.editorconfig b/.editorconfig index c8247e5496..ccbd29fd03 100644 --- a/.editorconfig +++ b/.editorconfig @@ -48,6 +48,11 @@ indent_size = 2 end_of_line = lf indent_size = 2 +# YAML files +[*.yml] +end_of_line = lf +indent_size = 2 + # Dotnet code style settings: [*.{cs,vb}] # Member can be made 'readonly' diff --git a/.github/workflows/pkgs-delete.yml b/.github/workflows/pkgs-delete.yml index 2dc580953d..75bbd9455e 100644 --- a/.github/workflows/pkgs-delete.yml +++ b/.github/workflows/pkgs-delete.yml @@ -61,78 +61,53 @@ jobs: shell: python - delete-git-pkgs: - name: Delete Old Nuget Packages + delete-git-docker-pkgs: + name: Delete Old Docker Images runs-on: ubuntu-latest steps: - - name: Delete Neo.Cryptography.BLS12_381 Package - uses: actions/delete-package-versions@v4 - with: - package-name: Neo.Cryptography.BLS12_381 - package-type: nuget - min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" - token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Delete Neo.VM Package - uses: actions/delete-package-versions@v4 - with: - package-name: Neo.VM - package-type: nuget - min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" - token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Delete Neo.Json Package - uses: actions/delete-package-versions@v4 - with: - package-name: Neo.Json - package-type: nuget - min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" - token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Delete Neo.IO Package - uses: actions/delete-package-versions@v4 - with: - package-name: Neo.IO - package-type: nuget - min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" - token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Delete Neo Package (nuget) - uses: actions/delete-package-versions@v4 - with: - package-name: Neo - package-type: nuget - min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" - token: "${{ secrets.GITHUB_TOKEN }}" - - name: Delete Neo Package (docker) uses: actions/delete-package-versions@v4 + continue-on-error: true with: package-name: Neo package-type: docker min-versions-to-keep: 1 - delete-only-pre-release-versions: "true" token: "${{ secrets.GITHUB_TOKEN }}" - - name: Delete Neo.ConsoleService Package - uses: actions/delete-package-versions@v4 - with: - package-name: Neo.ConsoleService - package-type: nuget - min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" - token: "${{ secrets.GITHUB_TOKEN }}" + delete-git-nuget-pkgs: + name: Delete Old Nuget Packages + strategy: + matrix: + pkgs: + - "Neo.Plugins.StatesDumper" + - "Neo.Plugins.StateService" + - "Neo.Plugins.Storage.LevelDBStore" + - "Neo.Plugins.Storage.RocksDBStore" + - "Neo.Plugins.StorageDumper" + - "Neo.Plugins.TokensTracker" + - "Neo.Wallets.SQLite" + - "Neo.Consensus.DBFT" + - "Neo.ConsoleService" + - "Neo.Cryptography.MPT" + - "Neo.Extensions" + - "Neo.Network.RPC.RpcClient" + - "Neo.Plugins.ApplicationLogs" + - "Neo.Plugins.OracleService" + - "Neo.Plugins.RpcServer" + - "Neo.Cryptography.BLS12_381" + - "Neo.VM" + - "Neo.Json" + - "Neo.IO" + - "Neo" + runs-on: ubuntu-latest - - name: Delete Neo.Extensions Package + steps: + - name: Delete ${{ matrix.pkgs }} Package uses: actions/delete-package-versions@v4 + continue-on-error: true with: - package-name: Neo.Extensions + package-name: ${{ matrix.pkgs }} package-type: nuget min-versions-to-keep: 3 delete-only-pre-release-versions: "true"