From d421c59df6136c5630906e984929acd17f996bf7 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 4 Sep 2024 08:43:20 -0400 Subject: [PATCH 01/11] Added Builders with tests --- .../Builders/TransactionAttributesBuilder.cs | 74 ++++++++++++ src/Neo/Builders/TransactionBuilder.cs | 108 ++++++++++++++++++ src/Neo/Builders/WitnessBuilder.cs | 71 ++++++++++++ .../Builders/UT_TransactionBuilder.cs | 85 ++++++++++++++ 4 files changed, 338 insertions(+) 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 tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs diff --git a/src/Neo/Builders/TransactionAttributesBuilder.cs b/src/Neo/Builders/TransactionAttributesBuilder.cs new file mode 100644 index 0000000000..47806ef8c8 --- /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 conflictAction) + { + var conflicts = new Conflicts(); + conflictAction(conflicts); + _attributes = [.. _attributes, conflicts]; + return this; + } + + public TransactionAttributesBuilder AddOracleResponse(Action oracleResponseAction) + { + var oracleResponse = new OracleResponse(); + oracleResponseAction(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..0d6f3b9b48 --- /dev/null +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -0,0 +1,108 @@ +// 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 byte _version = 0; + private uint _nonce = (uint)new Random().Next(); + private uint _systemFee = 0; + private uint _networkFee = 0; + private uint _validUntilBlock = 0; + private byte[] _script = []; + private TransactionAttribute[] _attributes = []; + private Signer[] _signers = []; + private Witness[] _witnesses = []; + + private TransactionBuilder() { } + + public static TransactionBuilder CreateEmpty() + { + return new TransactionBuilder(); + } + + public TransactionBuilder Version(byte version) + { + _version = version; + return this; + } + + public TransactionBuilder Nonce(uint nonce) + { + _nonce = nonce; + return this; + } + + public TransactionBuilder SystemFee(uint systemFee) + { + _systemFee = systemFee; + return this; + } + + public TransactionBuilder NetworkFee(uint networkFee) + { + _networkFee = networkFee; + return this; + } + + public TransactionBuilder ValidUntil(uint blockIndex) + { + _validUntilBlock = blockIndex; + return this; + } + + public TransactionBuilder AttachSystem(Action scriptBuilder) + { + var sb = new ScriptBuilder(); + scriptBuilder(sb); + _script = sb.ToArray(); + return this; + } + + public TransactionBuilder AddAttributes(Action transactionAttributeBuilder) + { + var ab = TransactionAttributesBuilder.CreateEmpty(); + transactionAttributeBuilder(ab); + _attributes = ab.Build(); + return this; + } + + public TransactionBuilder AddWitness(Action witnessBuilder) + { + var wb = WitnessBuilder.CreateEmpty(); + witnessBuilder(wb); + _witnesses = [.. _witnesses, wb.Build()]; + return this; + } + + public Transaction Build() + { + return new Transaction() + { + Version = _version, + Nonce = _nonce, + SystemFee = _systemFee, + NetworkFee = _networkFee, + ValidUntilBlock = _validUntilBlock, + Script = _script, + Attributes = _attributes, + Signers = _signers, + Witnesses = _witnesses + }; + } + } +} diff --git a/src/Neo/Builders/WitnessBuilder.cs b/src/Neo/Builders/WitnessBuilder.cs new file mode 100644 index 0000000000..16da83589b --- /dev/null +++ b/src/Neo/Builders/WitnessBuilder.cs @@ -0,0 +1,71 @@ +// 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.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +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 scriptBuilder) + { + var sb = new ScriptBuilder(); + scriptBuilder(sb); + _invocationScript = sb.ToArray(); + return this; + } + + public WitnessBuilder AddSigner(Action scriptBuilder) + { + var sb = new ScriptBuilder(); + scriptBuilder(sb); + _verificationScript = sb.ToArray(); + return this; + } + + public WitnessBuilder AddSigner(Action contract) + { + var c = new Contract(); + contract(c); + _verificationScript = c.Script; + return this; + } + + public WitnessBuilder AddSigner(ECPoint publicKey) + { + _verificationScript = Contract.CreateSignatureRedeemScript(publicKey); + return this; + } + + public Witness Build() + { + return new Witness() + { + InvocationScript = _invocationScript, + VerificationScript = _verificationScript + }; + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs new file mode 100644 index 0000000000..0338331b6e --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs @@ -0,0 +1,85 @@ +// 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 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 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); + } + } +} From a4c71f3c4bbb6bd60b060fe1a1599170a3c67b74 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Wed, 4 Sep 2024 14:47:57 -0400 Subject: [PATCH 02/11] Added SignerBuilder and started WitnessRuleBuilder --- src/Neo/Builders/SignerBuilder.cs | 55 +++++++++++++++ src/Neo/Builders/TransactionBuilder.cs | 65 +++++++++--------- src/Neo/Builders/WitnessBuilder.cs | 22 +++--- src/Neo/Builders/WitnessRuleBuilder.cs | 30 +++++++++ .../Builders/UT_TransactionBuilder.cs | 67 +++++++++++++++++++ 5 files changed, 191 insertions(+), 48 deletions(-) create mode 100644 src/Neo/Builders/SignerBuilder.cs create mode 100644 src/Neo/Builders/WitnessRuleBuilder.cs diff --git a/src/Neo/Builders/SignerBuilder.cs b/src/Neo/Builders/SignerBuilder.cs new file mode 100644 index 0000000000..4b98713c1b --- /dev/null +++ b/src/Neo/Builders/SignerBuilder.cs @@ -0,0 +1,55 @@ +// 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; + +namespace Neo.Builders +{ + public sealed class SignerBuilder + { + private readonly Signer _signer = new Signer() + { + AllowedContracts = [], + AllowedGroups = [], + Rules = [], + Scopes = WitnessScope.None, + }; + + private SignerBuilder(UInt160 scriptHash) + { + _signer.Account = scriptHash; + } + + public static SignerBuilder Create(UInt160 scriptHash) + { + return new SignerBuilder(scriptHash); + } + + 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; + } + } +} diff --git a/src/Neo/Builders/TransactionBuilder.cs b/src/Neo/Builders/TransactionBuilder.cs index 0d6f3b9b48..744dac5c71 100644 --- a/src/Neo/Builders/TransactionBuilder.cs +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -18,15 +18,13 @@ namespace Neo.Builders public sealed class TransactionBuilder { - private byte _version = 0; - private uint _nonce = (uint)new Random().Next(); - private uint _systemFee = 0; - private uint _networkFee = 0; - private uint _validUntilBlock = 0; - private byte[] _script = []; - private TransactionAttribute[] _attributes = []; - private Signer[] _signers = []; - private Witness[] _witnesses = []; + private readonly Transaction _tx = new() + { + Script = new[] { (byte)OpCode.RET }, + Attributes = [], + Signers = [], + Witnesses = [], + }; private TransactionBuilder() { } @@ -37,72 +35,69 @@ public static TransactionBuilder CreateEmpty() public TransactionBuilder Version(byte version) { - _version = version; + _tx.Version = version; return this; } public TransactionBuilder Nonce(uint nonce) { - _nonce = nonce; + _tx.Nonce = nonce; return this; } public TransactionBuilder SystemFee(uint systemFee) { - _systemFee = systemFee; + _tx.SystemFee = systemFee; return this; } public TransactionBuilder NetworkFee(uint networkFee) { - _networkFee = networkFee; + _tx.NetworkFee = networkFee; return this; } public TransactionBuilder ValidUntil(uint blockIndex) { - _validUntilBlock = blockIndex; + _tx.ValidUntilBlock = blockIndex; return this; } - public TransactionBuilder AttachSystem(Action scriptBuilder) + public TransactionBuilder AttachSystem(Action action) { var sb = new ScriptBuilder(); - scriptBuilder(sb); - _script = sb.ToArray(); + action(sb); + _tx.Script = sb.ToArray(); return this; } - public TransactionBuilder AddAttributes(Action transactionAttributeBuilder) + public TransactionBuilder AddAttributes(Action action) { var ab = TransactionAttributesBuilder.CreateEmpty(); - transactionAttributeBuilder(ab); - _attributes = ab.Build(); + action(ab); + _tx.Attributes = ab.Build(); + return this; + } + + public TransactionBuilder AddWitness(Action action) + { + var wb = WitnessBuilder.CreateEmpty(); + action(wb); + _tx.Witnesses = [.. _tx.Witnesses, wb.Build()]; return this; } - public TransactionBuilder AddWitness(Action witnessBuilder) + public TransactionBuilder AddWitness(Action action) { var wb = WitnessBuilder.CreateEmpty(); - witnessBuilder(wb); - _witnesses = [.. _witnesses, wb.Build()]; + action(wb, _tx); + _tx.Witnesses = [.. _tx.Witnesses, wb.Build()]; return this; } public Transaction Build() { - return new Transaction() - { - Version = _version, - Nonce = _nonce, - SystemFee = _systemFee, - NetworkFee = _networkFee, - ValidUntilBlock = _validUntilBlock, - Script = _script, - Attributes = _attributes, - Signers = _signers, - Witnesses = _witnesses - }; + return _tx; } } } diff --git a/src/Neo/Builders/WitnessBuilder.cs b/src/Neo/Builders/WitnessBuilder.cs index 16da83589b..6657287de6 100644 --- a/src/Neo/Builders/WitnessBuilder.cs +++ b/src/Neo/Builders/WitnessBuilder.cs @@ -9,9 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; -using Neo.SmartContract; using Neo.VM; using System; @@ -37,25 +35,23 @@ public WitnessBuilder AddInvocation(Action scriptBuilder) return this; } - public WitnessBuilder AddSigner(Action scriptBuilder) + public WitnessBuilder AddInvocation(byte[] bytes) { - var sb = new ScriptBuilder(); - scriptBuilder(sb); - _verificationScript = sb.ToArray(); + _invocationScript = bytes; return this; } - public WitnessBuilder AddSigner(Action contract) + public WitnessBuilder AddVerification(Action scriptBuilder) { - var c = new Contract(); - contract(c); - _verificationScript = c.Script; + var sb = new ScriptBuilder(); + scriptBuilder(sb); + _verificationScript = sb.ToArray(); return this; } - public WitnessBuilder AddSigner(ECPoint publicKey) + public WitnessBuilder AddVerification(byte[] bytes) { - _verificationScript = Contract.CreateSignatureRedeemScript(publicKey); + _verificationScript = bytes; return this; } @@ -64,7 +60,7 @@ public Witness Build() return new Witness() { InvocationScript = _invocationScript, - VerificationScript = _verificationScript + VerificationScript = _verificationScript, }; } } diff --git a/src/Neo/Builders/WitnessRuleBuilder.cs b/src/Neo/Builders/WitnessRuleBuilder.cs new file mode 100644 index 0000000000..32ed2d4fcc --- /dev/null +++ b/src/Neo/Builders/WitnessRuleBuilder.cs @@ -0,0 +1,30 @@ +// 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; + +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); + } + } +} diff --git a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs index 0338331b6e..e76f24e7e0 100644 --- a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs +++ b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs @@ -11,6 +11,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Builders; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.Wallets; using System; using System.Collections.Generic; using System.Linq; @@ -81,5 +84,69 @@ public void TestValidUntilBlock() 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 TestEmptyTransaction() + { + var tx = TransactionBuilder.CreateEmpty() + .Build(); + + Assert.IsNotNull(tx.Hash); + } } } From 4582df68dd56fde7265a5190c87678943c9bf84f Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 5 Sep 2024 07:56:24 -0400 Subject: [PATCH 03/11] Added `WitnessConditionBuilder` with tests --- src/Neo/Builders/WitnessConditionBuilder.cs | 140 +++++++++++++ src/Neo/Builders/WitnessRuleBuilder.cs | 5 + .../Builders/UT_WitnessConditionBuilder.cs | 186 ++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 src/Neo/Builders/WitnessConditionBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs diff --git a/src/Neo/Builders/WitnessConditionBuilder.cs b/src/Neo/Builders/WitnessConditionBuilder.cs new file mode 100644 index 0000000000..ab1f5af1b9 --- /dev/null +++ b/src/Neo/Builders/WitnessConditionBuilder.cs @@ -0,0 +1,140 @@ +// 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; + +namespace Neo.Builders +{ + public sealed class WitnessConditionBuilder + { + WitnessCondition _condition; + + private WitnessConditionBuilder() { } + + public static WitnessConditionBuilder Create() + { + return new WitnessConditionBuilder(); + } + + public WitnessConditionBuilder And() + { + var condition = new AndCondition() { Expressions = [] }; + + if (_condition is NotCondition notCondition) + notCondition.Expression = condition; + else + _condition = condition; + + return this; + } + + public WitnessConditionBuilder Boolean(bool expression) + { + var condition = new BooleanCondition() { Expression = expression }; + + SetConditionWithOtherConditions(condition); + + return this; + } + + public WitnessConditionBuilder CalledByContract(UInt160 hash) + { + var condition = new CalledByContractCondition() { Hash = hash }; + + SetConditionWithOtherConditions(condition); + + return this; + } + + public WitnessConditionBuilder CalledByEntry() + { + var condition = new CalledByEntryCondition(); + + SetConditionWithOtherConditions(condition); + + return this; + } + + public WitnessConditionBuilder CalledByGroup(ECPoint publicKey) + { + var condition = new CalledByGroupCondition() { Group = publicKey }; + + SetConditionWithOtherConditions(condition); + + return this; + } + + public WitnessConditionBuilder Group(ECPoint publicKey) + { + var condition = new GroupCondition() { Group = publicKey }; + + SetConditionWithOtherConditions(condition); + + return this; + } + + public WitnessConditionBuilder Not() + { + var condition = new NotCondition(); + + if (_condition is AndCondition andCondition) + andCondition.Expressions = [.. andCondition.Expressions, condition]; + else + _condition = condition; + + return this; + } + + public WitnessConditionBuilder Or() + { + var condition = new OrCondition() { Expressions = [] }; + + if (_condition is NotCondition notCondition) + notCondition.Expression = condition; + else + _condition = condition; + + return this; + } + + public WitnessConditionBuilder ScriptHash(UInt160 scriptHash) + { + var condition = new ScriptHashCondition() { Hash = scriptHash }; + + SetConditionWithOtherConditions(condition); + + return this; + } + + public WitnessCondition Build() + { + return _condition; + } + + private void SetConditionWithOtherConditions(WitnessCondition condition) + { + if (_condition is AndCondition andCondition) + andCondition.Expressions = [.. andCondition.Expressions, condition]; + else if (_condition is OrCondition orCondition) + orCondition.Expressions = [.. orCondition.Expressions, condition]; + else if (_condition is NotCondition notCondition) + { + if (notCondition.Expression is AndCondition a) + a.Expressions = [.. a.Expressions, condition]; + else if (notCondition.Expression is OrCondition o) + o.Expressions = [.. o.Expressions, condition]; + } + else + _condition = condition; + } + } +} diff --git a/src/Neo/Builders/WitnessRuleBuilder.cs b/src/Neo/Builders/WitnessRuleBuilder.cs index 32ed2d4fcc..85b340a7e1 100644 --- a/src/Neo/Builders/WitnessRuleBuilder.cs +++ b/src/Neo/Builders/WitnessRuleBuilder.cs @@ -26,5 +26,10 @@ public static WitnessRuleBuilder Create(WitnessRuleAction action) { return new WitnessRuleBuilder(action); } + + public WitnessRuleBuilder AddCondition(UInt160 contractHash) + { + return this; + } } } diff --git a/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs new file mode 100644 index 0000000000..c0e34881ea --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs @@ -0,0 +1,186 @@ +// 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() + .CalledByContract(expectedContractHash) + .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 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() + .And() + .CalledByContract(expectedContractHash) + .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() + .Or() + .CalledByContract(expectedContractHash) + .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); + } + } +} From abcc0bbe09a2723522543a9109a8121f2b58be19 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 5 Sep 2024 08:07:29 -0400 Subject: [PATCH 04/11] Added more logic --- src/Neo/Builders/SignerBuilder.cs | 14 ++++++++++++++ src/Neo/Builders/TransactionAttributesBuilder.cs | 8 ++++---- src/Neo/Builders/TransactionBuilder.cs | 16 ++++++++-------- src/Neo/Builders/WitnessBuilder.cs | 8 ++++---- src/Neo/Builders/WitnessRuleBuilder.cs | 12 ++++++++++-- 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/Neo/Builders/SignerBuilder.cs b/src/Neo/Builders/SignerBuilder.cs index 4b98713c1b..89c1789ff4 100644 --- a/src/Neo/Builders/SignerBuilder.cs +++ b/src/Neo/Builders/SignerBuilder.cs @@ -11,6 +11,7 @@ using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; +using System; namespace Neo.Builders { @@ -51,5 +52,18 @@ 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 index 47806ef8c8..9eb4ae99e3 100644 --- a/src/Neo/Builders/TransactionAttributesBuilder.cs +++ b/src/Neo/Builders/TransactionAttributesBuilder.cs @@ -26,18 +26,18 @@ public static TransactionAttributesBuilder CreateEmpty() return new TransactionAttributesBuilder(); } - public TransactionAttributesBuilder AddConflict(Action conflictAction) + public TransactionAttributesBuilder AddConflict(Action config) { var conflicts = new Conflicts(); - conflictAction(conflicts); + config(conflicts); _attributes = [.. _attributes, conflicts]; return this; } - public TransactionAttributesBuilder AddOracleResponse(Action oracleResponseAction) + public TransactionAttributesBuilder AddOracleResponse(Action config) { var oracleResponse = new OracleResponse(); - oracleResponseAction(oracleResponse); + config(oracleResponse); _attributes = [.. _attributes, oracleResponse]; return this; } diff --git a/src/Neo/Builders/TransactionBuilder.cs b/src/Neo/Builders/TransactionBuilder.cs index 744dac5c71..6338c09b8f 100644 --- a/src/Neo/Builders/TransactionBuilder.cs +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -63,34 +63,34 @@ public TransactionBuilder ValidUntil(uint blockIndex) return this; } - public TransactionBuilder AttachSystem(Action action) + public TransactionBuilder AttachSystem(Action config) { var sb = new ScriptBuilder(); - action(sb); + config(sb); _tx.Script = sb.ToArray(); return this; } - public TransactionBuilder AddAttributes(Action action) + public TransactionBuilder AddAttributes(Action config) { var ab = TransactionAttributesBuilder.CreateEmpty(); - action(ab); + config(ab); _tx.Attributes = ab.Build(); return this; } - public TransactionBuilder AddWitness(Action action) + public TransactionBuilder AddWitness(Action config) { var wb = WitnessBuilder.CreateEmpty(); - action(wb); + config(wb); _tx.Witnesses = [.. _tx.Witnesses, wb.Build()]; return this; } - public TransactionBuilder AddWitness(Action action) + public TransactionBuilder AddWitness(Action config) { var wb = WitnessBuilder.CreateEmpty(); - action(wb, _tx); + config(wb, _tx); _tx.Witnesses = [.. _tx.Witnesses, wb.Build()]; return this; } diff --git a/src/Neo/Builders/WitnessBuilder.cs b/src/Neo/Builders/WitnessBuilder.cs index 6657287de6..a114551518 100644 --- a/src/Neo/Builders/WitnessBuilder.cs +++ b/src/Neo/Builders/WitnessBuilder.cs @@ -27,10 +27,10 @@ public static WitnessBuilder CreateEmpty() return new WitnessBuilder(); } - public WitnessBuilder AddInvocation(Action scriptBuilder) + public WitnessBuilder AddInvocation(Action config) { var sb = new ScriptBuilder(); - scriptBuilder(sb); + config(sb); _invocationScript = sb.ToArray(); return this; } @@ -41,10 +41,10 @@ public WitnessBuilder AddInvocation(byte[] bytes) return this; } - public WitnessBuilder AddVerification(Action scriptBuilder) + public WitnessBuilder AddVerification(Action config) { var sb = new ScriptBuilder(); - scriptBuilder(sb); + config(sb); _verificationScript = sb.ToArray(); return this; } diff --git a/src/Neo/Builders/WitnessRuleBuilder.cs b/src/Neo/Builders/WitnessRuleBuilder.cs index 85b340a7e1..ee94ce2727 100644 --- a/src/Neo/Builders/WitnessRuleBuilder.cs +++ b/src/Neo/Builders/WitnessRuleBuilder.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Neo.Network.P2P.Payloads; +using System; namespace Neo.Builders { @@ -27,9 +28,16 @@ public static WitnessRuleBuilder Create(WitnessRuleAction action) return new WitnessRuleBuilder(action); } - public WitnessRuleBuilder AddCondition(UInt160 contractHash) + public void AddCondition(Action config) { - return this; + var cb = WitnessConditionBuilder.Create(); + config(cb); + _rule.Condition = cb.Build(); + } + + public WitnessRule Build() + { + return _rule; } } } From 48ddcc2224c4ae59e437f3cc3675916a8688b79b Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 5 Sep 2024 08:09:58 -0400 Subject: [PATCH 05/11] Fixed `SignerBuilder` class --- src/Neo/Builders/SignerBuilder.cs | 12 ++++++++---- src/Neo/Builders/TransactionBuilder.cs | 8 ++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Neo/Builders/SignerBuilder.cs b/src/Neo/Builders/SignerBuilder.cs index 89c1789ff4..76b70f1efe 100644 --- a/src/Neo/Builders/SignerBuilder.cs +++ b/src/Neo/Builders/SignerBuilder.cs @@ -19,20 +19,24 @@ public sealed class SignerBuilder { private readonly Signer _signer = new Signer() { + Account = UInt160.Zero, AllowedContracts = [], AllowedGroups = [], Rules = [], Scopes = WitnessScope.None, }; - private SignerBuilder(UInt160 scriptHash) + private SignerBuilder() { } + + public static SignerBuilder CreateEmpty() { - _signer.Account = scriptHash; + return new SignerBuilder(); } - public static SignerBuilder Create(UInt160 scriptHash) + public SignerBuilder Account(UInt160 scriptHash) { - return new SignerBuilder(scriptHash); + _signer.Account = scriptHash; + return this; } public SignerBuilder AllowContract(UInt160 contractHash) diff --git a/src/Neo/Builders/TransactionBuilder.cs b/src/Neo/Builders/TransactionBuilder.cs index 6338c09b8f..bc14f75d65 100644 --- a/src/Neo/Builders/TransactionBuilder.cs +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -95,6 +95,14 @@ public TransactionBuilder AddWitness(Action config) 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; From a4da230b0c608d8a5b0b6313f333b46c251d39c6 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 5 Sep 2024 13:21:06 -0400 Subject: [PATCH 06/11] Code touch ups --- src/Neo/Builders/WitnessConditionBuilder.cs | 3 ++ .../Builders/UT_TransactionBuilder.cs | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Neo/Builders/WitnessConditionBuilder.cs b/src/Neo/Builders/WitnessConditionBuilder.cs index ab1f5af1b9..c6e4790281 100644 --- a/src/Neo/Builders/WitnessConditionBuilder.cs +++ b/src/Neo/Builders/WitnessConditionBuilder.cs @@ -117,6 +117,9 @@ public WitnessConditionBuilder ScriptHash(UInt160 scriptHash) public WitnessCondition Build() { + if (_condition is null) + return new BooleanCondition() { Expression = false }; + return _condition; } diff --git a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs index e76f24e7e0..7e76303db7 100644 --- a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs +++ b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs @@ -11,7 +11,11 @@ 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; @@ -148,5 +152,42 @@ public void TestEmptyTransaction() Assert.IsNotNull(tx.Hash); } + + [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); + } } } From 675b6f8333c2124a652560e06576f40d70234f04 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 5 Sep 2024 17:54:43 -0400 Subject: [PATCH 07/11] Added more tests --- src/Neo/Builders/WitnessRuleBuilder.cs | 3 +- .../Builders/UT_SignerBuilder.cs | 101 ++++++++++++++++++ .../UT_TransactionAttributesBuilder.cs | 92 ++++++++++++++++ .../Builders/UT_TransactionBuilder.cs | 26 +++-- .../Builders/UT_WitnessBuilder.cs | 91 ++++++++++++++++ .../Builders/UT_WitnessRuleBuilder.cs | 50 +++++++++ 6 files changed, 353 insertions(+), 10 deletions(-) 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_WitnessBuilder.cs create mode 100644 tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs diff --git a/src/Neo/Builders/WitnessRuleBuilder.cs b/src/Neo/Builders/WitnessRuleBuilder.cs index ee94ce2727..7c05a524d6 100644 --- a/src/Neo/Builders/WitnessRuleBuilder.cs +++ b/src/Neo/Builders/WitnessRuleBuilder.cs @@ -28,11 +28,12 @@ public static WitnessRuleBuilder Create(WitnessRuleAction action) return new WitnessRuleBuilder(action); } - public void AddCondition(Action config) + public WitnessRuleBuilder AddCondition(Action config) { var cb = WitnessConditionBuilder.Create(); config(cb); _rule.Condition = cb.Build(); + return this; } public WitnessRule Build() 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 index 7e76303db7..17eb5796ad 100644 --- a/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs +++ b/tests/Neo.UnitTests/Builders/UT_TransactionBuilder.cs @@ -29,6 +29,23 @@ 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() { @@ -144,15 +161,6 @@ public void TestWitnessWithTransactionParameter() .Build(); } - [TestMethod] - public void TestEmptyTransaction() - { - var tx = TransactionBuilder.CreateEmpty() - .Build(); - - Assert.IsNotNull(tx.Hash); - } - [TestMethod] public void TestSigner() { 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_WitnessRuleBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs new file mode 100644 index 0000000000..1956a44688 --- /dev/null +++ b/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs @@ -0,0 +1,50 @@ +// 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); + } + } +} From c8593ab35da38b98b5f71a69e265cb2bd6bbc6ae Mon Sep 17 00:00:00 2001 From: Shargon Date: Sat, 7 Sep 2024 06:34:22 +0800 Subject: [PATCH 08/11] Update src/Neo/Builders/TransactionBuilder.cs --- src/Neo/Builders/TransactionBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Neo/Builders/TransactionBuilder.cs b/src/Neo/Builders/TransactionBuilder.cs index bc14f75d65..38813c6d7b 100644 --- a/src/Neo/Builders/TransactionBuilder.cs +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -17,7 +17,6 @@ namespace Neo.Builders { public sealed class TransactionBuilder { - private readonly Transaction _tx = new() { Script = new[] { (byte)OpCode.RET }, From 522026627117b4f21d3ebb85346c7bccc7f787f5 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Sat, 7 Sep 2024 09:23:59 -0400 Subject: [PATCH 09/11] Fixed `And` `Or` and `Not` conditions --- src/Neo/Builders/AndConditionBuilder.cs | 90 +++++++++++++++++++ src/Neo/Builders/OrConditionBuilder.cs | 90 +++++++++++++++++++ src/Neo/Builders/WitnessConditionBuilder.cs | 72 +++++++-------- .../Builders/UT_WitnessConditionBuilder.cs | 56 +++++++++--- .../Builders/UT_WitnessRuleBuilder.cs | 19 ++++ 5 files changed, 274 insertions(+), 53 deletions(-) create mode 100644 src/Neo/Builders/AndConditionBuilder.cs create mode 100644 src/Neo/Builders/OrConditionBuilder.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/WitnessConditionBuilder.cs b/src/Neo/Builders/WitnessConditionBuilder.cs index c6e4790281..41cfc05163 100644 --- a/src/Neo/Builders/WitnessConditionBuilder.cs +++ b/src/Neo/Builders/WitnessConditionBuilder.cs @@ -11,6 +11,7 @@ using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads.Conditions; +using System; namespace Neo.Builders { @@ -20,19 +21,22 @@ public sealed class WitnessConditionBuilder private WitnessConditionBuilder() { } + private WitnessConditionBuilder(WitnessCondition condition) + { + _condition = condition; + } + public static WitnessConditionBuilder Create() { return new WitnessConditionBuilder(); } - public WitnessConditionBuilder And() + public WitnessConditionBuilder And(Action config) { - var condition = new AndCondition() { Expressions = [] }; + var acb = AndConditionBuilder.CreateEmpty(); + config(acb); - if (_condition is NotCondition notCondition) - notCondition.Expression = condition; - else - _condition = condition; + _condition = acb.Build(); return this; } @@ -41,7 +45,7 @@ public WitnessConditionBuilder Boolean(bool expression) { var condition = new BooleanCondition() { Expression = expression }; - SetConditionWithOtherConditions(condition); + _condition = condition; return this; } @@ -50,7 +54,7 @@ public WitnessConditionBuilder CalledByContract(UInt160 hash) { var condition = new CalledByContractCondition() { Hash = hash }; - SetConditionWithOtherConditions(condition); + _condition = condition; return this; } @@ -59,7 +63,7 @@ public WitnessConditionBuilder CalledByEntry() { var condition = new CalledByEntryCondition(); - SetConditionWithOtherConditions(condition); + _condition = condition; return this; } @@ -68,7 +72,7 @@ public WitnessConditionBuilder CalledByGroup(ECPoint publicKey) { var condition = new CalledByGroupCondition() { Group = publicKey }; - SetConditionWithOtherConditions(condition); + _condition = condition; return this; } @@ -77,31 +81,32 @@ public WitnessConditionBuilder Group(ECPoint publicKey) { var condition = new GroupCondition() { Group = publicKey }; - SetConditionWithOtherConditions(condition); + _condition = condition; return this; } - public WitnessConditionBuilder Not() + public WitnessConditionBuilder Not(Action config) { - var condition = new NotCondition(); + var wcb = new WitnessConditionBuilder(); + config(wcb); + + var condition = new NotCondition() + { + Expression = wcb.Build() + }; - if (_condition is AndCondition andCondition) - andCondition.Expressions = [.. andCondition.Expressions, condition]; - else - _condition = condition; + _condition = condition; return this; } - public WitnessConditionBuilder Or() + public WitnessConditionBuilder Or(Action config) { - var condition = new OrCondition() { Expressions = [] }; + var ocb = OrConditionBuilder.CreateEmpty(); + config(ocb); - if (_condition is NotCondition notCondition) - notCondition.Expression = condition; - else - _condition = condition; + _condition = ocb.Build(); return this; } @@ -110,7 +115,7 @@ public WitnessConditionBuilder ScriptHash(UInt160 scriptHash) { var condition = new ScriptHashCondition() { Hash = scriptHash }; - SetConditionWithOtherConditions(condition); + _condition = condition; return this; } @@ -118,26 +123,9 @@ public WitnessConditionBuilder ScriptHash(UInt160 scriptHash) public WitnessCondition Build() { if (_condition is null) - return new BooleanCondition() { Expression = false }; + return new BooleanCondition() { Expression = true }; return _condition; } - - private void SetConditionWithOtherConditions(WitnessCondition condition) - { - if (_condition is AndCondition andCondition) - andCondition.Expressions = [.. andCondition.Expressions, condition]; - else if (_condition is OrCondition orCondition) - orCondition.Expressions = [.. orCondition.Expressions, condition]; - else if (_condition is NotCondition notCondition) - { - if (notCondition.Expression is AndCondition a) - a.Expressions = [.. a.Expressions, condition]; - else if (notCondition.Expression is OrCondition o) - o.Expressions = [.. o.Expressions, condition]; - } - else - _condition = condition; - } } } diff --git a/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs index c0e34881ea..4378aa2bfa 100644 --- a/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs +++ b/tests/Neo.UnitTests/Builders/UT_WitnessConditionBuilder.cs @@ -30,9 +30,11 @@ public void TestAndCondition() var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); var expectedContractHash = UInt160.Zero; var condition = WitnessConditionBuilder.Create() - .And() - .CalledByContract(expectedContractHash) - .CalledByGroup(expectedPublicKey) + .And(and => + { + and.CalledByContract(expectedContractHash); + and.CalledByGroup(expectedPublicKey); + }) .Build(); var actual = condition as AndCondition; @@ -46,6 +48,30 @@ public void TestAndCondition() 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() { @@ -139,10 +165,14 @@ public void TestNotConditionWithAndCondition() var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); var expectedContractHash = UInt160.Zero; var condition = WitnessConditionBuilder.Create() - .Not() - .And() - .CalledByContract(expectedContractHash) - .CalledByGroup(expectedPublicKey) + .Not(not => + { + not.And(and => + { + and.CalledByContract(expectedContractHash); + and.CalledByGroup(expectedPublicKey); + }); + }) .Build(); var actual = condition as NotCondition; @@ -164,10 +194,14 @@ public void TestNotConditionWithOrCondition() var expectedPublicKey = ECPoint.Parse("021821807f923a3da004fb73871509d7635bcc05f41edef2a3ca5c941d8bbc1231", ECCurve.Secp256r1); var expectedContractHash = UInt160.Zero; var condition = WitnessConditionBuilder.Create() - .Not() - .Or() - .CalledByContract(expectedContractHash) - .CalledByGroup(expectedPublicKey) + .Not(not => + { + not.Or(or => + { + or.CalledByContract(expectedContractHash); + or.CalledByGroup(expectedPublicKey); + }); + }) .Build(); var actual = condition as NotCondition; diff --git a/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs b/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs index 1956a44688..1b87e2c52f 100644 --- a/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs +++ b/tests/Neo.UnitTests/Builders/UT_WitnessRuleBuilder.cs @@ -46,5 +46,24 @@ public void TestCondition() 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 3a14a1f2989fce825b3ee162a741b36f7310059e Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 16 Sep 2024 04:51:36 -0400 Subject: [PATCH 10/11] Fixed Memory leak --- src/Neo/Builders/TransactionBuilder.cs | 8 +++++++- src/Neo/Builders/WitnessBuilder.cs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Neo/Builders/TransactionBuilder.cs b/src/Neo/Builders/TransactionBuilder.cs index 38813c6d7b..2a1a7a9424 100644 --- a/src/Neo/Builders/TransactionBuilder.cs +++ b/src/Neo/Builders/TransactionBuilder.cs @@ -64,12 +64,18 @@ public TransactionBuilder ValidUntil(uint blockIndex) public TransactionBuilder AttachSystem(Action config) { - var sb = new ScriptBuilder(); + 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(); diff --git a/src/Neo/Builders/WitnessBuilder.cs b/src/Neo/Builders/WitnessBuilder.cs index a114551518..35e52790e9 100644 --- a/src/Neo/Builders/WitnessBuilder.cs +++ b/src/Neo/Builders/WitnessBuilder.cs @@ -29,7 +29,7 @@ public static WitnessBuilder CreateEmpty() public WitnessBuilder AddInvocation(Action config) { - var sb = new ScriptBuilder(); + using var sb = new ScriptBuilder(); config(sb); _invocationScript = sb.ToArray(); return this; @@ -43,7 +43,7 @@ public WitnessBuilder AddInvocation(byte[] bytes) public WitnessBuilder AddVerification(Action config) { - var sb = new ScriptBuilder(); + using var sb = new ScriptBuilder(); config(sb); _verificationScript = sb.ToArray(); return this; From 68469d91d838531331cd544f67452f26e6cec429 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Tue, 17 Sep 2024 00:22:48 -0400 Subject: [PATCH 11/11] Added error message for Witness scripts --- src/Neo/Builders/WitnessBuilder.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Neo/Builders/WitnessBuilder.cs b/src/Neo/Builders/WitnessBuilder.cs index 35e52790e9..e555195688 100644 --- a/src/Neo/Builders/WitnessBuilder.cs +++ b/src/Neo/Builders/WitnessBuilder.cs @@ -29,6 +29,9 @@ public static WitnessBuilder CreateEmpty() 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(); @@ -37,12 +40,18 @@ public WitnessBuilder AddInvocation(Action config) 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(); @@ -51,6 +60,9 @@ public WitnessBuilder AddVerification(Action config) public WitnessBuilder AddVerification(byte[] bytes) { + if (_verificationScript.Length > 0) + throw new InvalidOperationException("Verification script already exists."); + _verificationScript = bytes; return this; }