From dd427c74a505b32c174c99cf8dc7a6bf831ad146 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 30 May 2019 00:47:16 +0200 Subject: [PATCH 01/46] Fee= VerificationCost+ApplicationCost --- neo/Ledger/Blockchain.cs | 2 +- neo/Network/P2P/Payloads/BlockBase.cs | 3 ++- neo/Network/P2P/Payloads/ConsensusPayload.cs | 3 ++- neo/Network/P2P/Payloads/Transaction.cs | 10 ++++++++-- neo/SmartContract/Helper.cs | 6 ++++-- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index d00bde3c39..7e40e1b1bf 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -433,7 +433,7 @@ private void Persist(Block block) BlockIndex = block.Index, Transaction = tx }); - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.Gas)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.AvailableGas)) { engine.LoadScript(tx.Script); if (!engine.Execute().HasFlag(VMState.FAULT)) diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 769b457e9e..8feac83ea2 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -95,7 +95,8 @@ public virtual bool Verify(Snapshot snapshot) if (prev_header == null) return false; if (prev_header.Index + 1 != Index) return false; if (prev_header.Timestamp >= Timestamp) return false; - if (!this.VerifyWitness(snapshot, 1_00000000)) return false; + var gas = 1_00000000L; + if (!this.VerifyWitness(snapshot, ref gas)) return false; return true; } } diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index cdbf459418..81bb5aaf3b 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -109,7 +109,8 @@ public bool Verify(Snapshot snapshot) { if (BlockIndex <= snapshot.Height) return false; - return this.VerifyWitness(snapshot, 0_02000000); + var gas = 0_02000000L; + return this.VerifyWitness(snapshot, ref gas); } } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 52a6fc867a..7bb489853c 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -35,6 +35,8 @@ public class Transaction : IEquatable, IInventory public TransactionAttribute[] Attributes; public Witness Witness { get; set; } + public long AvailableGas; + /// /// The NetworkFee for the transaction divided by its Size. /// Note that this property must be used with care. Getting the value of this property multiple times will return the same result. The value of this property can only be obtained after the transaction has been completely built (no longer modified). @@ -206,11 +208,15 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) if (NativeContract.Policy.GetBlockedAccounts(snapshot).Contains(Sender)) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - BigInteger fee = Gas + NetworkFee; + var fee = Gas + NetworkFee; if (balance < fee) return false; fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); if (balance < fee) return false; - return this.VerifyWitness(snapshot, VerificationGasLimited); + + AvailableGas = Gas; + var ret = this.VerifyWitness(snapshot, ref AvailableGas); + + return ret; } } } diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index ddd16de13e..3d3bb14000 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -246,7 +246,7 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Default.Hash160(script)); } - internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long gas) + internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, ref long availableGas) { UInt160 hash; try @@ -267,12 +267,14 @@ internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapsho { if (hash != verifiable.Witness.ScriptHash) return false; } - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, availableGas)) { engine.LoadScript(verification); engine.LoadScript(verifiable.Witness.InvocationScript); if (engine.Execute().HasFlag(VMState.FAULT)) return false; if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; + + availableGas -= engine.GasConsumed; } return true; } From fe9a505bc26cd4657e01b5efb5c3acb551b8c9f5 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 30 May 2019 10:58:18 +0200 Subject: [PATCH 02/46] NetworkFee for verification --- neo/Ledger/Blockchain.cs | 2 +- neo/Network/P2P/Payloads/BlockBase.cs | 3 +-- neo/Network/P2P/Payloads/ConsensusPayload.cs | 3 +-- neo/Network/P2P/Payloads/Transaction.cs | 14 +++++++------- neo/SmartContract/Helper.cs | 4 +--- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 7e40e1b1bf..d00bde3c39 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -433,7 +433,7 @@ private void Persist(Block block) BlockIndex = block.Index, Transaction = tx }); - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.AvailableGas)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.Gas)) { engine.LoadScript(tx.Script); if (!engine.Execute().HasFlag(VMState.FAULT)) diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 8feac83ea2..d402df6ca8 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -95,8 +95,7 @@ public virtual bool Verify(Snapshot snapshot) if (prev_header == null) return false; if (prev_header.Index + 1 != Index) return false; if (prev_header.Timestamp >= Timestamp) return false; - var gas = 1_00000000L; - if (!this.VerifyWitness(snapshot, ref gas)) return false; + if (!this.VerifyWitness(snapshot, 1_00000000L)) return false; return true; } } diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index 81bb5aaf3b..84f66e9f34 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -109,8 +109,7 @@ public bool Verify(Snapshot snapshot) { if (BlockIndex <= snapshot.Height) return false; - var gas = 0_02000000L; - return this.VerifyWitness(snapshot, ref gas); + return this.VerifyWitness(snapshot, 0_02000000L); } } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 7bb489853c..a247929286 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -23,20 +23,23 @@ public class Transaction : IEquatable, IInventory /// Maximum number of attributes that can be contained within a transaction /// private const int MaxTransactionAttributes = 16; - private const long VerificationGasLimited = 0_10000000; public byte Version; public uint Nonce; public byte[] Script; public UInt160 Sender; + /// + /// Distributed to NEO holders. + /// public long Gas; + /// + /// Distributed to consensus nodes. + /// public long NetworkFee; public uint ValidUntilBlock; public TransactionAttribute[] Attributes; public Witness Witness { get; set; } - public long AvailableGas; - /// /// The NetworkFee for the transaction divided by its Size. /// Note that this property must be used with care. Getting the value of this property multiple times will return the same result. The value of this property can only be obtained after the transaction has been completely built (no longer modified). @@ -213,10 +216,7 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); if (balance < fee) return false; - AvailableGas = Gas; - var ret = this.VerifyWitness(snapshot, ref AvailableGas); - - return ret; + return this.VerifyWitness(snapshot, NetworkFee); } } } diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 3d3bb14000..8380c848e7 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -246,7 +246,7 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Default.Hash160(script)); } - internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, ref long availableGas) + internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long availableGas) { UInt160 hash; try @@ -273,8 +273,6 @@ internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapsho engine.LoadScript(verifiable.Witness.InvocationScript); if (engine.Execute().HasFlag(VMState.FAULT)) return false; if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; - - availableGas -= engine.GasConsumed; } return true; } From 7cda593e248ba33139249cc934331b11fbe748c9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 30 May 2019 10:58:59 +0200 Subject: [PATCH 03/46] Clean --- neo/Network/P2P/Payloads/BlockBase.cs | 2 +- neo/Network/P2P/Payloads/ConsensusPayload.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index d402df6ca8..769b457e9e 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -95,7 +95,7 @@ public virtual bool Verify(Snapshot snapshot) if (prev_header == null) return false; if (prev_header.Index + 1 != Index) return false; if (prev_header.Timestamp >= Timestamp) return false; - if (!this.VerifyWitness(snapshot, 1_00000000L)) return false; + if (!this.VerifyWitness(snapshot, 1_00000000)) return false; return true; } } diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index 84f66e9f34..cdbf459418 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -109,7 +109,7 @@ public bool Verify(Snapshot snapshot) { if (BlockIndex <= snapshot.Height) return false; - return this.VerifyWitness(snapshot, 0_02000000L); + return this.VerifyWitness(snapshot, 0_02000000); } } } From d690f7b5046d129108f85c4d62c6815f64c5b159 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 30 May 2019 10:59:47 +0200 Subject: [PATCH 04/46] Clean --- neo/SmartContract/Helper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 8380c848e7..ddd16de13e 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -246,7 +246,7 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Default.Hash160(script)); } - internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long availableGas) + internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long gas) { UInt160 hash; try @@ -267,7 +267,7 @@ internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapsho { if (hash != verifiable.Witness.ScriptHash) return false; } - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, availableGas)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) { engine.LoadScript(verification); engine.LoadScript(verifiable.Witness.InvocationScript); From 1a1247a6f89f4a333662974a175c13b8b2f50c05 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 31 May 2019 10:25:11 +0200 Subject: [PATCH 05/46] Update --- neo/Network/P2P/Payloads/Transaction.cs | 5 +++-- neo/SmartContract/Helper.cs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index a247929286..9dfaa35ad2 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -206,7 +206,8 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) return false; int size = Size; if (size > MaxTransactionSize) return false; - if (size > NativeContract.Policy.GetMaxLowPriorityTransactionSize(snapshot) && NetworkFee / size < NativeContract.Policy.GetFeePerByte(snapshot)) + var feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); + if (size > NativeContract.Policy.GetMaxLowPriorityTransactionSize(snapshot) && NetworkFee / size < feePerByte) return false; if (NativeContract.Policy.GetBlockedAccounts(snapshot).Contains(Sender)) return false; @@ -216,7 +217,7 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); if (balance < fee) return false; - return this.VerifyWitness(snapshot, NetworkFee); + return this.VerifyWitness(snapshot, NetworkFee - (size * feePerByte)); } } } diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index ddd16de13e..9c366bb426 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -248,6 +248,8 @@ public static UInt160 ToScriptHash(this byte[] script) internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long gas) { + if (gas < 0) return false; + UInt160 hash; try { From c0675d0d71893b3f0953423ac4e381e4855da204 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 2 Jun 2019 20:46:16 +0200 Subject: [PATCH 06/46] Remove priority from Policy and MemoryPool --- neo.UnitTests/UT_MemoryPool.cs | 196 ++++++--------------- neo.UnitTests/UT_Policy.cs | 80 +-------- neo/Ledger/MemoryPool.cs | 128 ++++---------- neo/Network/P2P/Payloads/Transaction.cs | 2 +- neo/SmartContract/Native/PolicyContract.cs | 52 ------ 5 files changed, 92 insertions(+), 366 deletions(-) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index a35c85fcc7..8518238435 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -61,165 +61,88 @@ private Transaction CreateTransactionWithFee(long fee) return mock.Object; } - private Transaction CreateHighPriorityTransaction() + private Transaction CreateTransaction() { return CreateTransactionWithFee(LongRandom(100000, 100000000, TestUtils.TestRandom)); } - private Transaction CreateLowPriorityTransaction() - { - long rNetFee = LongRandom(0, 10000, TestUtils.TestRandom); - // [0,0.001] GAS a fee lower than the threshold of 0.001 GAS (not enough to be a high priority TX) - return CreateTransactionWithFee(rNetFee); - } - - private bool IsLowPriority(Transaction tx) - { - return tx.FeePerByte < 1000; - } - - private void AddTransactions(int count, bool isHighPriority = false) + private void AddTransactions(int count) { for (int i = 0; i < count; i++) { - var txToAdd = isHighPriority ? CreateHighPriorityTransaction() : CreateLowPriorityTransaction(); + var txToAdd = CreateTransaction(); Console.WriteLine($"created tx: {txToAdd.Hash}"); _unit.TryAdd(txToAdd.Hash, txToAdd); } } - private void AddLowPriorityTransactions(int count) => AddTransactions(count); - public void AddHighPriorityTransactions(int count) => AddTransactions(count, true); - - [TestMethod] - public void LowPriorityCapacityTest() - { - // Add over the capacity items, verify that the verified count increases each time - AddLowPriorityTransactions(50); - _unit.VerifiedCount.ShouldBeEquivalentTo(50); - AddLowPriorityTransactions(51); - Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} LowPrioCount {_unit.SortedLowPrioTxCount} HighPrioCount {_unit.SortedHighPrioTxCount}"); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(100); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - - _unit.VerifiedCount.ShouldBeEquivalentTo(100); - _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); - _unit.Count.ShouldBeEquivalentTo(100); - } [TestMethod] - public void HighPriorityCapacityTest() + public void CapacityTest() { // Add over the capacity items, verify that the verified count increases each time - AddHighPriorityTransactions(101); + AddTransactions(101); - Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} LowPrioCount {_unit.SortedLowPrioTxCount} HighPrioCount {_unit.SortedHighPrioTxCount}"); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(100); + Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} Count {_unit.SortedTxCount}"); + _unit.SortedTxCount.ShouldBeEquivalentTo(100); _unit.VerifiedCount.ShouldBeEquivalentTo(100); _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); _unit.Count.ShouldBeEquivalentTo(100); } - [TestMethod] - public void HighPriorityPushesOutLowPriority() - { - // Add over the capacity items, verify that the verified count increases each time - AddLowPriorityTransactions(70); - AddHighPriorityTransactions(40); - - Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} LowPrioCount {_unit.SortedLowPrioTxCount} HighPrioCount {_unit.SortedHighPrioTxCount}"); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(40); - _unit.Count.ShouldBeEquivalentTo(100); - } - - [TestMethod] - public void LowPriorityDoesNotPushOutHighPrority() - { - AddHighPriorityTransactions(70); - AddLowPriorityTransactions(40); - - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(30); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(70); - _unit.Count.ShouldBeEquivalentTo(100); - } - [TestMethod] public void BlockPersistMovesTxToUnverifiedAndReverification() { - AddHighPriorityTransactions(70); - AddLowPriorityTransactions(30); + AddTransactions(70); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(70); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(30); + _unit.SortedTxCount.ShouldBeEquivalentTo(70); var block = new Block { Transactions = _unit.GetSortedVerifiedTransactions().Take(10) - .Concat(_unit.GetSortedVerifiedTransactions().Where(x => IsLowPriority(x)).Take(5)).ToArray() + .Concat(_unit.GetSortedVerifiedTransactions().Take(5)).ToArray() }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(25); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(60); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(9); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(1); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(51); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(24); + _unit.SortedTxCount.ShouldBeEquivalentTo(9); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(51); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(18); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(2); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(42); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(23); + _unit.SortedTxCount.ShouldBeEquivalentTo(18); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(42); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(27); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(3); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(33); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(22); + _unit.SortedTxCount.ShouldBeEquivalentTo(27); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(33); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(36); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(4); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(24); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(21); + _unit.SortedTxCount.ShouldBeEquivalentTo(36); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(24); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(45); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(5); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(15); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(20); + _unit.SortedTxCount.ShouldBeEquivalentTo(45); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(15); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(54); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(6); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(6); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(19); + _unit.SortedTxCount.ShouldBeEquivalentTo(54); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(6); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(10); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(15); + _unit.SortedTxCount.ShouldBeEquivalentTo(60); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(20); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(5); + _unit.SortedTxCount.ShouldBeEquivalentTo(60); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(25); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(60); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); } private void verifyTransactionsSortedDescending(IEnumerable transactions) @@ -248,8 +171,7 @@ private void verifyTransactionsSortedDescending(IEnumerable transac [TestMethod] public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() { - AddLowPriorityTransactions(50); - AddHighPriorityTransactions(50); + AddTransactions(100); var sortedVerifiedTxs = _unit.GetSortedVerifiedTransactions().ToList(); // verify all 100 transactions are returned in sorted order @@ -260,37 +182,31 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var block = new Block { Transactions = new Transaction[0] }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(50); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(50); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(100); // We can verify the order they are re-verified by reverifying 2 at a time while (_unit.UnVerifiedCount > 0) { - _unit.GetVerifiedAndUnverifiedTransactions(out IEnumerable sortedVerifiedTransactions, - out IEnumerable sortedUnverifiedTransactions); + _unit.GetVerifiedAndUnverifiedTransactions(out var sortedVerifiedTransactions, out var sortedUnverifiedTransactions); sortedVerifiedTransactions.Count().ShouldBeEquivalentTo(0); var sortedUnverifiedArray = sortedUnverifiedTransactions.ToArray(); verifyTransactionsSortedDescending(sortedUnverifiedArray); - var maxHighPriorityTransaction = sortedUnverifiedArray.First(); - var maxLowPriorityTransaction = sortedUnverifiedArray.First(tx => IsLowPriority(tx)); + var maxTransaction = sortedUnverifiedArray.First(); + var minTransaction = sortedUnverifiedArray.Last(); // reverify 1 high priority and 1 low priority transaction _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot()); var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray(); - verifiedTxs.Length.ShouldBeEquivalentTo(2); - verifiedTxs[0].ShouldBeEquivalentTo(maxHighPriorityTransaction); - verifiedTxs[1].ShouldBeEquivalentTo(maxLowPriorityTransaction); - var blockWith2Tx = new Block { Transactions = new[] { maxHighPriorityTransaction, maxLowPriorityTransaction } }; + verifiedTxs.Length.ShouldBeEquivalentTo(1); + verifiedTxs[0].ShouldBeEquivalentTo(maxTransaction); + var blockWith2Tx = new Block { Transactions = new[] { maxTransaction, minTransaction } }; // verify and remove the 2 transactions from the verified pool _unit.UpdatePoolForBlockPersisted(blockWith2Tx, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); } - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); } void VerifyCapacityThresholdForAttemptingToAddATransaction() @@ -306,11 +222,11 @@ void VerifyCapacityThresholdForAttemptingToAddATransaction() [TestMethod] public void VerifyCanTransactionFitInPoolWorksAsIntended() { - AddLowPriorityTransactions(100); + AddTransactions(100); VerifyCapacityThresholdForAttemptingToAddATransaction(); - AddHighPriorityTransactions(50); + AddTransactions(50); VerifyCapacityThresholdForAttemptingToAddATransaction(); - AddHighPriorityTransactions(50); + AddTransactions(50); VerifyCapacityThresholdForAttemptingToAddATransaction(); } @@ -321,31 +237,27 @@ public void CapacityTestWithUnverifiedHighProirtyTransactions() // low priority transactions // Fill pool with high priority transactions - AddHighPriorityTransactions(99); + AddTransactions(99); // move all to unverified var block = new Block { Transactions = new Transaction[0] }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); - _unit.CanTransactionFitInPool(CreateLowPriorityTransaction()).ShouldBeEquivalentTo(true); - AddHighPriorityTransactions(1); - _unit.CanTransactionFitInPool(CreateLowPriorityTransaction()).ShouldBeEquivalentTo(false); + _unit.CanTransactionFitInPool(CreateTransaction()).ShouldBeEquivalentTo(true); + AddTransactions(1); + _unit.CanTransactionFitInPool(CreateTransactionWithFee(0)).ShouldBeEquivalentTo(false); } [TestMethod] public void TestInvalidateAll() { - AddHighPriorityTransactions(30); - AddLowPriorityTransactions(60); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(30); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(60); + AddTransactions(30); + + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(30); _unit.InvalidateAllTransactions(); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(30); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); } } } diff --git a/neo.UnitTests/UT_Policy.cs b/neo.UnitTests/UT_Policy.cs index da93eca368..09a09b20ba 100644 --- a/neo.UnitTests/UT_Policy.cs +++ b/neo.UnitTests/UT_Policy.cs @@ -32,20 +32,12 @@ public void Check_Initialize() NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - (keyCount + 5).Should().Be(snapshot.Storages.GetChangeSet().Count()); + (keyCount + 3).Should().Be(snapshot.Storages.GetChangeSet().Count()); var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(512); - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(20); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionSize"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(256); - ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(1000); @@ -90,76 +82,6 @@ public void Check_SetMaxTransactionsPerBlock() ret.GetBigInteger().Should().Be(1); } - [TestMethod] - public void Check_SetMaxLowPriorityTransactionsPerBlock() - { - var snapshot = Store.GetSnapshot().Clone(); - - // Fake blockchain - - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - - // Without signature - - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), - "setMaxLowPriorityTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(20); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), - "setMaxLowPriorityTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(1); - } - - [TestMethod] - public void Check_SetMaxLowPriorityTransactionSize() - { - var snapshot = Store.GetSnapshot().Clone(); - - // Fake blockchain - - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - - // Without signature - - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), - "setMaxLowPriorityTransactionSize", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionSize"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(256); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), - "setMaxLowPriorityTransactionSize", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionSize"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(1); - } - [TestMethod] public void Check_SetFeePerByte() { diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index c3748ef7ec..4fb9a3174c 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -1,15 +1,15 @@ -using Neo.Network.P2P.Payloads; +using Akka.Util.Internal; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.Plugins; +using Neo.SmartContract.Native; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; -using Akka.Util.Internal; -using Neo.Network.P2P; -using Neo.Persistence; -using Neo.Plugins; -using Neo.SmartContract.Native; namespace Neo.Ledger { @@ -20,12 +20,10 @@ public class MemoryPool : IReadOnlyCollection private const int BlocksTillRebroadcastHighPriorityPoolTx = 10; private int RebroadcastMultiplierThreshold => Capacity / 10; - private static readonly double MaxSecondsToReverifyHighPrioTx = (double) Blockchain.SecondsPerBlock / 3; - private static readonly double MaxSecondsToReverifyLowPrioTx = (double) Blockchain.SecondsPerBlock / 5; + private static readonly double MaxSecondsToReverifyTx = (double)Blockchain.SecondsPerBlock / 3; // These two are not expected to be hit, they are just safegaurds. - private static readonly double MaxSecondsToReverifyHighPrioTxPerIdle = (double) Blockchain.SecondsPerBlock / 15; - private static readonly double MaxSecondsToReverifyLowPrioTxPerIdle = (double) Blockchain.SecondsPerBlock / 30; + private static readonly double MaxSecondsToReverifyTxPerIdle = (double)Blockchain.SecondsPerBlock / 15; private readonly NeoSystem _system; @@ -44,33 +42,25 @@ public class MemoryPool : IReadOnlyCollection /// private readonly Dictionary _unsortedTransactions = new Dictionary(); /// - /// Stores the verified high priority sorted transactins currently in the pool. - /// - private readonly SortedSet _sortedHighPrioTransactions = new SortedSet(); - /// - /// Stores the verified low priority sorted transactions currently in the pool. + /// Stores the verified sorted transactins currently in the pool. /// - private readonly SortedSet _sortedLowPrioTransactions = new SortedSet(); + private readonly SortedSet _sortedTransactions = new SortedSet(); /// /// Store the unverified transactions currently in the pool. /// /// Transactions in this data structure were valid in some prior block, but may no longer be valid. /// The top ones that could make it into the next block get verified and moved into the verified data structures - /// (_unsortedTransactions, _sortedLowPrioTransactions, and _sortedHighPrioTransactions) after each block. + /// (_unsortedTransactions, and _sortedTransactions) after each block. /// private readonly Dictionary _unverifiedTransactions = new Dictionary(); - private readonly SortedSet _unverifiedSortedHighPriorityTransactions = new SortedSet(); - private readonly SortedSet _unverifiedSortedLowPriorityTransactions = new SortedSet(); + private readonly SortedSet _unverifiedSortedTransactions = new SortedSet(); // Internal methods to aid in unit testing - internal int SortedHighPrioTxCount => _sortedHighPrioTransactions.Count; - internal int SortedLowPrioTxCount => _sortedLowPrioTransactions.Count; - internal int UnverifiedSortedHighPrioTxCount => _unverifiedSortedHighPriorityTransactions.Count; - internal int UnverifiedSortedLowPrioTxCount => _unverifiedSortedLowPriorityTransactions.Count; + internal int SortedTxCount => _sortedTransactions.Count; + internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; private int _maxTxPerBlock; - private int _maxLowPriorityTxPerBlock; private long _feePerByte; /// @@ -113,7 +103,6 @@ public MemoryPool(NeoSystem system, int capacity) internal void LoadPolicy(Snapshot snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); - _maxLowPriorityTxPerBlock = (int)NativeContract.Policy.GetMaxLowPriorityTransactionsPerBlock(snapshot); _feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); } @@ -129,8 +118,7 @@ public bool ContainsKey(UInt256 hash) _txRwLock.EnterReadLock(); try { - return _unsortedTransactions.ContainsKey(hash) - || _unverifiedTransactions.ContainsKey(hash); + return _unsortedTransactions.ContainsKey(hash) || _unverifiedTransactions.ContainsKey(hash); } finally { @@ -192,10 +180,8 @@ public void GetVerifiedAndUnverifiedTransactions(out IEnumerable ve _txRwLock.EnterReadLock(); try { - verifiedTransactions = _sortedHighPrioTransactions.Reverse().Select(p => p.Tx) - .Concat(_sortedLowPrioTransactions.Reverse().Select(p => p.Tx)).ToArray(); - unverifiedTransactions = _unverifiedSortedHighPriorityTransactions.Reverse().Select(p => p.Tx) - .Concat(_unverifiedSortedLowPriorityTransactions.Reverse().Select(p => p.Tx)).ToArray(); + verifiedTransactions = _sortedTransactions.Reverse().Select(p => p.Tx).ToArray(); + unverifiedTransactions = _unverifiedSortedTransactions.Reverse().Select(p => p.Tx).ToArray(); } finally { @@ -208,9 +194,7 @@ public IEnumerable GetSortedVerifiedTransactions() _txRwLock.EnterReadLock(); try { - return _sortedHighPrioTransactions.Reverse().Select(p => p.Tx) - .Concat(_sortedLowPrioTransactions.Reverse().Select(p => p.Tx)) - .ToArray(); + return _sortedTransactions.Reverse().Select(p => p.Tx).ToArray(); } finally { @@ -239,25 +223,16 @@ private PoolItem GetLowestFeeTransaction(SortedSet verifiedTxSorted, private PoolItem GetLowestFeeTransaction(out Dictionary unsortedTxPool, out SortedSet sortedPool) { - var minItem = GetLowestFeeTransaction(_sortedLowPrioTransactions, _unverifiedSortedLowPriorityTransactions, - out sortedPool); - - if (minItem != null) - { - unsortedTxPool = Object.ReferenceEquals(sortedPool, _unverifiedSortedLowPriorityTransactions) - ? _unverifiedTransactions : _unsortedTransactions; - return minItem; - } + sortedPool = null; try { - return GetLowestFeeTransaction(_sortedHighPrioTransactions, _unverifiedSortedHighPriorityTransactions, - out sortedPool); + return GetLowestFeeTransaction(_sortedTransactions, _unverifiedSortedTransactions, out sortedPool); } finally { - unsortedTxPool = Object.ReferenceEquals(sortedPool, _unverifiedSortedHighPriorityTransactions) - ? _unverifiedTransactions : _unsortedTransactions; + unsortedTxPool = Object.ReferenceEquals(sortedPool, _unverifiedSortedTransactions) + ? _unverifiedTransactions : _unsortedTransactions; } } @@ -269,12 +244,6 @@ internal bool CanTransactionFitInPool(Transaction tx) return GetLowestFeeTransaction(out _, out _).CompareTo(tx) <= 0; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool IsLowPriority(Transaction tx) - { - return tx.FeePerByte < _feePerByte; - } - /// /// Adds an already verified transaction to the memory pool. /// @@ -295,9 +264,8 @@ internal bool TryAdd(UInt256 hash, Transaction tx) try { _unsortedTransactions.Add(hash, poolItem); + _sortedTransactions.Add(poolItem); - SortedSet pool = IsLowPriority(tx) ? _sortedLowPrioTransactions : _sortedHighPrioTransactions; - pool.Add(poolItem); if (Count > Capacity) removedTransactions = RemoveOverCapacity(); } @@ -338,9 +306,8 @@ private bool TryRemoveVerified(UInt256 hash, out PoolItem item) return false; _unsortedTransactions.Remove(hash); - SortedSet pool = IsLowPriority(item.Tx) - ? _sortedLowPrioTransactions : _sortedHighPrioTransactions; - pool.Remove(item); + _sortedTransactions.Remove(item); + return true; } @@ -351,31 +318,22 @@ internal bool TryRemoveUnVerified(UInt256 hash, out PoolItem item) return false; _unverifiedTransactions.Remove(hash); - SortedSet pool = IsLowPriority(item.Tx) - ? _unverifiedSortedLowPriorityTransactions : _unverifiedSortedHighPriorityTransactions; - pool.Remove(item); + _unverifiedSortedTransactions.Remove(item); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void InvalidateVerifiedTransactions() { - foreach (PoolItem item in _sortedHighPrioTransactions) + foreach (PoolItem item in _sortedTransactions) { if (_unverifiedTransactions.TryAdd(item.Tx.Hash, item)) - _unverifiedSortedHighPriorityTransactions.Add(item); - } - - foreach (PoolItem item in _sortedLowPrioTransactions) - { - if (_unverifiedTransactions.TryAdd(item.Tx.Hash, item)) - _unverifiedSortedLowPriorityTransactions.Add(item); + _unverifiedSortedTransactions.Add(item); } // Clear the verified transactions now, since they all must be reverified. _unsortedTransactions.Clear(); - _sortedHighPrioTransactions.Clear(); - _sortedLowPrioTransactions.Clear(); + _sortedTransactions.Clear(); } // Note: this must only be called from a single thread (the Blockchain actor) @@ -405,11 +363,8 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) return; LoadPolicy(snapshot); - - ReverifyTransactions(_sortedHighPrioTransactions, _unverifiedSortedHighPriorityTransactions, - _maxTxPerBlock, MaxSecondsToReverifyHighPrioTx, snapshot); - ReverifyTransactions(_sortedLowPrioTransactions, _unverifiedSortedLowPriorityTransactions, - _maxLowPriorityTxPerBlock, MaxSecondsToReverifyLowPrioTx, snapshot); + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, + _maxTxPerBlock, MaxSecondsToReverifyTx, snapshot); } internal void InvalidateAllTransactions() @@ -446,7 +401,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, _txRwLock.EnterWriteLock(); try { - int blocksTillRebroadcast = Object.ReferenceEquals(unverifiedSortedTxPool, _sortedHighPrioTransactions) + int blocksTillRebroadcast = Object.ReferenceEquals(unverifiedSortedTxPool, _sortedTransactions) ? BlocksTillRebroadcastHighPriorityPoolTx : BlocksTillRebroadcastLowPriorityPoolTx; if (Count > RebroadcastMultiplierThreshold) @@ -504,23 +459,12 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, Snapsho if (Blockchain.Singleton.Height < Blockchain.Singleton.HeaderHeight) return false; - if (_unverifiedSortedHighPriorityTransactions.Count > 0) + if (_unverifiedSortedTransactions.Count > 0) { // Always leave at least 1 tx for low priority tx - int verifyCount = _sortedHighPrioTransactions.Count > _maxTxPerBlock || maxToVerify == 1 - ? 1 : maxToVerify - 1; - maxToVerify -= ReverifyTransactions(_sortedHighPrioTransactions, _unverifiedSortedHighPriorityTransactions, - verifyCount, MaxSecondsToReverifyHighPrioTxPerIdle, snapshot); - - if (maxToVerify == 0) maxToVerify++; - } - - if (_unverifiedSortedLowPriorityTransactions.Count > 0) - { - int verifyCount = _sortedLowPrioTransactions.Count > _maxLowPriorityTxPerBlock - ? 1 : maxToVerify; - ReverifyTransactions(_sortedLowPrioTransactions, _unverifiedSortedLowPriorityTransactions, - verifyCount, MaxSecondsToReverifyLowPrioTxPerIdle, snapshot); + int verifyCount = _sortedTransactions.Count > _maxTxPerBlock || maxToVerify == 1 ? 1 : maxToVerify - 1; + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, + verifyCount, MaxSecondsToReverifyTxPerIdle, snapshot); } return _unverifiedTransactions.Count > 0; diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 9dfaa35ad2..f1e501144e 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -207,7 +207,7 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) int size = Size; if (size > MaxTransactionSize) return false; var feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); - if (size > NativeContract.Policy.GetMaxLowPriorityTransactionSize(snapshot) && NetworkFee / size < feePerByte) + if (NetworkFee / size < feePerByte) return false; if (NativeContract.Policy.GetBlockedAccounts(snapshot).Contains(Sender)) return false; diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index e5f8dbeefe..03781a5cb7 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -18,8 +18,6 @@ public sealed class PolicyContract : NativeContract public override string ServiceName => "Neo.Native.Policy"; private const byte Prefix_MaxTransactionsPerBlock = 23; - private const byte Prefix_MaxLowPriorityTransactionsPerBlock = 34; - private const byte Prefix_MaxLowPriorityTransactionSize = 29; private const byte Prefix_FeePerByte = 10; private const byte Prefix_BlockedAccounts = 15; @@ -42,14 +40,6 @@ internal override bool Initialize(ApplicationEngine engine) { Value = BitConverter.GetBytes(512u) }); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxLowPriorityTransactionsPerBlock), new StorageItem - { - Value = BitConverter.GetBytes(20u) - }); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxLowPriorityTransactionSize), new StorageItem - { - Value = BitConverter.GetBytes(256u) - }); engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem { Value = BitConverter.GetBytes(1000L) @@ -72,28 +62,6 @@ public uint GetMaxTransactionsPerBlock(Snapshot snapshot) return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetMaxLowPriorityTransactionsPerBlock(ApplicationEngine engine, VMArray args) - { - return GetMaxLowPriorityTransactionsPerBlock(engine.Snapshot); - } - - public uint GetMaxLowPriorityTransactionsPerBlock(Snapshot snapshot) - { - return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxLowPriorityTransactionsPerBlock)].Value, 0); - } - - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetMaxLowPriorityTransactionSize(ApplicationEngine engine, VMArray args) - { - return GetMaxLowPriorityTransactionSize(engine.Snapshot); - } - - public uint GetMaxLowPriorityTransactionSize(Snapshot snapshot) - { - return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxLowPriorityTransactionSize)].Value, 0); - } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] private StackItem GetFeePerByte(ApplicationEngine engine, VMArray args) { @@ -126,26 +94,6 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray a return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] - private StackItem SetMaxLowPriorityTransactionsPerBlock(ApplicationEngine engine, VMArray args) - { - if (!CheckValidators(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxLowPriorityTransactionsPerBlock)); - storage.Value = BitConverter.GetBytes(value); - return true; - } - - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] - private StackItem SetMaxLowPriorityTransactionSize(ApplicationEngine engine, VMArray args) - { - if (!CheckValidators(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxLowPriorityTransactionSize)); - storage.Value = BitConverter.GetBytes(value); - return true; - } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) { From e8cc7a625db352711a62ac5a70e2c7e1343daf04 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 3 Jun 2019 09:39:23 +0200 Subject: [PATCH 07/46] Fix CalculateFee --- neo/Network/P2P/Payloads/Transaction.cs | 28 +++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index f1e501144e..b27b5e36fa 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -83,7 +83,7 @@ public void CalculateFees() }; _hash = null; long consumed; - using (ApplicationEngine engine = ApplicationEngine.Run(Script, this)) + using (ApplicationEngine engine = ApplicationEngine.Run(Script, this, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) throw new InvalidOperationException(); @@ -107,8 +107,32 @@ public void CalculateFees() } using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { + // Compute verification gas + + byte[] verification = Witness.VerificationScript; + if (verification.Length == 0) + { + verification = snapshot.Contracts.TryGet(Sender)?.Script; + if (verification is null) return; + } + else + { + if (Sender != Witness.ScriptHash) return; + } + + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) + { + engine.LoadScript(verification); + engine.LoadScript(Witness.InvocationScript); + if (engine.Execute().HasFlag(VMState.FAULT)) return; + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return; + consumed = engine.GasConsumed; + } + + // Compute fee per byte + long feeperbyte = NativeContract.Policy.GetFeePerByte(snapshot); - long fee = feeperbyte * Size; + long fee = consumed + (feeperbyte * Size); if (fee > NetworkFee) NetworkFee = fee; } From a3775cbf6ee24f51efa995ac29c99c56c96a4857 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 3 Jun 2019 15:57:19 +0200 Subject: [PATCH 08/46] Allow to cancel transactions with more Gas --- neo/Network/P2P/Payloads/Transaction.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index b27b5e36fa..07e0676bf5 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -238,8 +238,6 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); var fee = Gas + NetworkFee; if (balance < fee) return false; - fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); - if (balance < fee) return false; return this.VerifyWitness(snapshot, NetworkFee - (size * feePerByte)); } From d08d311297501dada912e56496d57a6394884172 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 6 Jun 2019 08:39:41 +0200 Subject: [PATCH 09/46] Revert change We need to test this first --- neo/Network/P2P/Payloads/Transaction.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 92d23113c0..5d90a83b41 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -238,7 +238,8 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); var fee = Gas + NetworkFee; if (balance < fee) return false; - + fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); + if (balance < fee) return false; return this.VerifyWitness(snapshot, NetworkFee - (size * feePerByte)); } } From fa51de3c1d8db61c6328822ff39e39330117a0c6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 6 Jun 2019 09:22:43 +0200 Subject: [PATCH 10/46] Add Cosigners --- .../Nep5NativeContractExtensions.cs | 17 +- neo.UnitTests/TestUtils.cs | 8 +- neo.UnitTests/TestVerifiable.cs | 8 +- neo.UnitTests/UT_Block.cs | 52 ++---- neo.UnitTests/UT_MemoryPool.cs | 4 +- neo.UnitTests/UT_PoolItem.cs | 6 +- neo.UnitTests/UT_Transaction.cs | 12 +- neo/Consensus/ConsensusContext.cs | 4 +- neo/Ledger/Blockchain.cs | 6 +- neo/Network/P2P/Payloads/BlockBase.cs | 10 +- neo/Network/P2P/Payloads/ConsensusPayload.cs | 7 +- neo/Network/P2P/Payloads/IVerifiable.cs | 4 +- neo/Network/P2P/Payloads/Transaction.cs | 65 +++---- .../P2P/Payloads/TransactionAttribute.cs | 21 ++- .../P2P/Payloads/TransactionAttributeUsage.cs | 1 + .../ContractParametersContext.cs | 175 +++++++++++------- neo/SmartContract/Helper.cs | 40 ++-- neo/SmartContract/InteropService.NEO.cs | 14 +- neo/SmartContract/InteropService.cs | 3 +- neo/Wallets/Wallet.cs | 15 +- 20 files changed, 259 insertions(+), 213 deletions(-) diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index 5f80114dc9..024f7646c0 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -15,29 +15,22 @@ public static class Nep5NativeContractExtensions { internal class ManualWitness : IVerifiable { - private readonly UInt160 _hashForVerify; + private readonly UInt160[] _hashForVerify; - public Witness Witness - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } + public Witness[] Witnesses => throw new NotImplementedException(); public int Size => 0; - public ManualWitness(UInt160 hashForVerify) + public ManualWitness(params UInt160[] hashForVerify) { - _hashForVerify = hashForVerify; + _hashForVerify = hashForVerify ?? new UInt160[0]; } public void Deserialize(BinaryReader reader) { } public void DeserializeUnsigned(BinaryReader reader) { } - public UInt160 GetScriptHashForVerification(Persistence.Snapshot snapshot) - { - return _hashForVerify; - } + public UInt160[] GetScriptHashesForVerifying(Persistence.Snapshot snapshot) => _hashForVerify; public void Serialize(BinaryWriter writer) { } diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 8660328bf7..52f41de2e7 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -28,11 +28,11 @@ public static Transaction GetTransaction() Script = new byte[1], Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - } + } } }; } @@ -86,11 +86,11 @@ public static Transaction CreateRandomHashTransaction() Script = randomBytes, Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - } + } } }; } diff --git a/neo.UnitTests/TestVerifiable.cs b/neo.UnitTests/TestVerifiable.cs index 78d3d314c0..dc37debb32 100644 --- a/neo.UnitTests/TestVerifiable.cs +++ b/neo.UnitTests/TestVerifiable.cs @@ -7,9 +7,9 @@ namespace Neo.UnitTests { public class TestVerifiable : IVerifiable { - private string testStr = "testStr"; + private readonly string testStr = "testStr"; - public Witness Witness { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public Witness[] Witnesses { get => throw new NotImplementedException(); } public int Size => throw new NotImplementedException(); @@ -23,7 +23,7 @@ public void DeserializeUnsigned(BinaryReader reader) throw new NotImplementedException(); } - public UInt160 GetScriptHashForVerification(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) { throw new NotImplementedException(); } @@ -35,7 +35,7 @@ public void Serialize(BinaryWriter writer) public void SerializeUnsigned(BinaryWriter writer) { - writer.Write((string) testStr); + writer.Write((string)testStr); } } } \ No newline at end of file diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index f7acf254be..dbabe1093e 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -28,12 +28,7 @@ public void Transactions_Get() public void Header_Get() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var merkRootVal, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 0); uut.Header.Should().NotBeNull(); uut.Header.PrevHash.Should().Be(val256); @@ -47,12 +42,7 @@ public void Header_Get() public void Size_Get() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 3 // block 9 + 1 uut.Size.Should().Be(109); @@ -62,31 +52,21 @@ public void Size_Get() public void Size_Get_1_Transaction() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); uut.Transactions = new[] { TestUtils.GetTransaction() }; - uut.Size.Should().Be(159); + uut.Size.Should().Be(160); } [TestMethod] public void Size_Get_3_Transaction() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); uut.Transactions = new[] { @@ -95,19 +75,14 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(259); + uut.Size.Should().Be(262); } [TestMethod] public void Serialize() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 1); byte[] data; using (MemoryStream stream = new MemoryStream()) @@ -119,7 +94,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) @@ -132,12 +107,7 @@ public void Serialize() public void Deserialize() { UInt256 val256 = UInt256.Zero; - UInt256 merkRoot; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(new Block(), val256, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); + TestUtils.SetupBlockWithValues(new Block(), val256, out var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid @@ -245,7 +215,7 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); jObj["hash"].AsString().Should().Be("0xe0b482b6e4c176c9af520dd7caa6d80a9aeaeb80d016e788c702b05f5ac5ba4b"); - jObj["size"].AsNumber().Should().Be(159); + jObj["size"].AsNumber().Should().Be(160); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); jObj["merkleroot"].AsString().Should().Be("0xc59eab3a287f74405b80325bc26c66ac7ef18d94c434a18d60950d3f098280f2"); @@ -260,7 +230,7 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; txObj[0]["hash"].AsString().Should().Be("0x7647acf1ef8a841b87f2369398a0e9f0ccde0ed9e835d980657103da6da59580"); - txObj[0]["size"].AsNumber().Should().Be(50); + txObj[0]["size"].AsNumber().Should().Be(51); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); txObj[0]["net_fee"].AsString().Should().Be("0"); diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 8518238435..3c577eeac1 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -53,11 +53,11 @@ private Transaction CreateTransactionWithFee(long fee) mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; mock.Object.Attributes = new TransactionAttribute[0]; - mock.Object.Witness = new Witness + mock.Object.Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - }; + } }; return mock.Object; } diff --git a/neo.UnitTests/UT_PoolItem.cs b/neo.UnitTests/UT_PoolItem.cs index 272dcf4084..107e5e8a9a 100644 --- a/neo.UnitTests/UT_PoolItem.cs +++ b/neo.UnitTests/UT_PoolItem.cs @@ -122,17 +122,17 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS Sender = UInt160.Zero, NetworkFee = networkFee, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - } + } } }; int diff = size - tx.Size; if (diff < 0) throw new ArgumentException(); if (diff > 0) - tx.Witness.VerificationScript = new byte[diff]; + tx.Witnesses[0].VerificationScript = new byte[diff]; return tx; } } diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index b192a2479d..4e442afcd8 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -55,16 +55,16 @@ public void Size_Get() uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; uut.Attributes = new TransactionAttribute[0]; - uut.Witness = new Witness + uut.Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - }; + } }; uut.Version.Should().Be(0); uut.Script.Length.Should().Be(32); uut.Script.GetVarSize().Should().Be(33); - uut.Size.Should().Be(81); + uut.Size.Should().Be(82); } [TestMethod] @@ -74,16 +74,16 @@ public void ToJson() uut.Sender = UInt160.Zero; uut.Gas = 4200000000; uut.Attributes = new TransactionAttribute[0]; - uut.Witness = new Witness + uut.Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - }; + } }; JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); jObj["hash"].AsString().Should().Be("0x38274692538dfecaae36f8fd518d92bae25607d491c40a8f927cc06bd97ab2c8"); - jObj["size"].AsNumber().Should().Be(81); + jObj["size"].AsNumber().Should().Be(82); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 3affe821f5..5fa53ce567 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -85,7 +85,7 @@ public Block CreateBlock() sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage().Signature); j++; } - Block.Witness = sc.GetWitness(); + Block.Witness = sc.GetWitnesses()[0]; Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray(); return Block; } @@ -202,7 +202,7 @@ private void SignPayload(ConsensusPayload payload) { return; } - payload.Witness = sc.GetWitness(); + payload.Witness = sc.GetWitnesses()[0]; } public ConsensusPayload MakePrepareRequest() diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index d00bde3c39..db31a8c6e2 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -154,11 +154,11 @@ private static Transaction DeployNativeContracts() Sender = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(), Gas = 0, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new[] { (byte)OpCode.PUSHT } - } + } } }; } @@ -280,7 +280,7 @@ private RelayResultReason OnNewBlock(Block block) block_cache_unverified.Remove(blockToPersist.Index); Persist(blockToPersist); - if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0,(15 - SecondsPerBlock)))) continue; + if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0, (15 - SecondsPerBlock)))) continue; // Empirically calibrated for relaying the most recent 2 blocks persisted with 15s network // Increase in the rate of 1 block per second in configurations with faster blocks diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 769b457e9e..7e6e1c7e10 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -19,6 +19,8 @@ public abstract class BlockBase : IVerifiable public UInt160 NextConsensus; public Witness Witness { get; set; } + Witness[] IVerifiable.Witnesses => new[] { Witness }; + private UInt256 _hash = null; public UInt256 Hash { @@ -50,12 +52,12 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) NextConsensus = reader.ReadSerializable(); } - UInt160 IVerifiable.GetScriptHashForVerification(Snapshot snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) { - if (PrevHash == UInt256.Zero) return Witness.ScriptHash; + if (PrevHash == UInt256.Zero) return new UInt160[] { Witness.ScriptHash }; Header prev_header = snapshot.GetHeader(PrevHash); if (prev_header == null) throw new InvalidOperationException(); - return prev_header.NextConsensus; + return new UInt160[] { prev_header.NextConsensus }; } public virtual void Serialize(BinaryWriter writer) @@ -95,7 +97,7 @@ public virtual bool Verify(Snapshot snapshot) if (prev_header == null) return false; if (prev_header.Index + 1 != Index) return false; if (prev_header.Timestamp >= Timestamp) return false; - if (!this.VerifyWitness(snapshot, 1_00000000)) return false; + if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; return true; } } diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index cdbf459418..122c92ab76 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -18,6 +18,7 @@ public class ConsensusPayload : IInventory public ushort ValidatorIndex; public byte[] Data; public Witness Witness { get; set; } + Witness[] IVerifiable.Witnesses => new[] { Witness }; private ConsensusMessage _deserializedMessage = null; public ConsensusMessage ConsensusMessage @@ -82,12 +83,12 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Data = reader.ReadVarBytes(); } - UInt160 IVerifiable.GetScriptHashForVerification(Snapshot snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) { ECPoint[] validators = NativeContract.NEO.GetNextBlockValidators(snapshot); if (validators.Length <= ValidatorIndex) throw new InvalidOperationException(); - return Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash(); + return new UInt160[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; } void ISerializable.Serialize(BinaryWriter writer) @@ -109,7 +110,7 @@ public bool Verify(Snapshot snapshot) { if (BlockIndex <= snapshot.Height) return false; - return this.VerifyWitness(snapshot, 0_02000000); + return this.VerifyWitnesses(snapshot, 0_02000000); } } } diff --git a/neo/Network/P2P/Payloads/IVerifiable.cs b/neo/Network/P2P/Payloads/IVerifiable.cs index 87a7dcb701..26ce90574c 100644 --- a/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/neo/Network/P2P/Payloads/IVerifiable.cs @@ -6,11 +6,11 @@ namespace Neo.Network.P2P.Payloads { public interface IVerifiable : ISerializable { - Witness Witness { get; set; } + Witness[] Witnesses { get; } void DeserializeUnsigned(BinaryReader reader); - UInt160 GetScriptHashForVerification(Snapshot snapshot); + UInt160[] GetScriptHashesForVerifying(Snapshot snapshot); void SerializeUnsigned(BinaryWriter writer); } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 5d90a83b41..4688d1c3ae 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -38,7 +38,7 @@ public class Transaction : IEquatable, IInventory public long NetworkFee; public uint ValidUntilBlock; public TransactionAttribute[] Attributes; - public Witness Witness { get; set; } + public Witness[] Witnesses { get; set; } /// /// The NetworkFee for the transaction divided by its Size. @@ -70,17 +70,13 @@ public UInt256 Hash sizeof(long) + //NetworkFee sizeof(uint) + //ValidUntilBlock Attributes.GetVarSize() + //Attributes - Witness.Size; //Witnesses + Witnesses.GetVarSize(); //Witnesses public void CalculateFees() { if (Sender is null) Sender = UInt160.Zero; if (Attributes is null) Attributes = new TransactionAttribute[0]; - if (Witness is null) Witness = new Witness - { - InvocationScript = new byte[65], - VerificationScript = new byte[39] - }; + if (Witnesses is null) Witnesses = new Witness[0]; _hash = null; long consumed; using (ApplicationEngine engine = ApplicationEngine.Run(Script, this, testMode: true)) @@ -109,24 +105,27 @@ public void CalculateFees() { // Compute verification gas - byte[] verification = Witness.VerificationScript; - if (verification.Length == 0) - { - verification = snapshot.Contracts.TryGet(Sender)?.Script; - if (verification is null) return; - } - else - { - if (Sender != Witness.ScriptHash) return; - } - - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) + foreach(var witness in Witnesses) { - engine.LoadScript(verification); - engine.LoadScript(Witness.InvocationScript); - if (engine.Execute().HasFlag(VMState.FAULT)) return; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return; - consumed = engine.GasConsumed; + byte[] verification = witness.VerificationScript; + if (verification.Length == 0) + { + verification = snapshot.Contracts.TryGet(Sender)?.Script; + if (verification is null) return; + } + else + { + if (Sender != witness.ScriptHash) return; + } + + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) + { + engine.LoadScript(verification); + engine.LoadScript(witness.InvocationScript); + if (engine.Execute().HasFlag(VMState.FAULT)) return; + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return; + consumed = engine.GasConsumed; + } } // Compute fee per byte @@ -141,7 +140,7 @@ public void CalculateFees() void ISerializable.Deserialize(BinaryReader reader) { DeserializeUnsigned(reader); - Witness = reader.ReadSerializable(); + Witnesses = reader.ReadSerializableArray(); } public void DeserializeUnsigned(BinaryReader reader) @@ -160,6 +159,8 @@ public void DeserializeUnsigned(BinaryReader reader) if (Gas + NetworkFee < Gas) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); + var cosigners = GetScriptHashesForVerifying(null); + if (cosigners.Distinct().Count() != cosigners.Length) throw new FormatException(); } public bool Equals(Transaction other) @@ -179,15 +180,17 @@ public override int GetHashCode() return Hash.GetHashCode(); } - public UInt160 GetScriptHashForVerification(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) { - return Sender; + var hashes = new HashSet { Sender }; + hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data))); + return hashes.OrderBy(p => p).ToArray(); } void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witness); + writer.Write(Witnesses); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) @@ -215,7 +218,7 @@ public JObject ToJson() json["net_fee"] = new BigDecimal(NetworkFee, (byte)NativeContract.GAS.Decimals).ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); - json["witness"] = Witness.ToJson(); + json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; } @@ -233,14 +236,14 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) var feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); if (NetworkFee / size < feePerByte) return false; - if (NativeContract.Policy.GetBlockedAccounts(snapshot).Contains(Sender)) + if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); var fee = Gas + NetworkFee; if (balance < fee) return false; fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); if (balance < fee) return false; - return this.VerifyWitness(snapshot, NetworkFee - (size * feePerByte)); + return this.VerifyWitnesses(snapshot, NetworkFee - (size * feePerByte)); } } } diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/neo/Network/P2P/Payloads/TransactionAttribute.cs index 2f384fa046..810f497ddd 100644 --- a/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -10,11 +10,25 @@ public class TransactionAttribute : ISerializable public TransactionAttributeUsage Usage; public byte[] Data; - public int Size => sizeof(TransactionAttributeUsage) + Data.GetVarSize(); + public int Size + { + get + { + if (Usage == TransactionAttributeUsage.Cosigner) + return sizeof(TransactionAttributeUsage) + 20; + else + return sizeof(TransactionAttributeUsage) + Data.GetVarSize(); + } + } void ISerializable.Deserialize(BinaryReader reader) { Usage = (TransactionAttributeUsage)reader.ReadByte(); + if (Usage == TransactionAttributeUsage.Cosigner) + Data = reader.ReadBytes(20); + else if (Usage == TransactionAttributeUsage.Url) + Data = reader.ReadVarBytes(252); + else if (!Enum.IsDefined(typeof(TransactionAttributeUsage), Usage)) throw new FormatException(); Data = reader.ReadVarBytes(252); @@ -23,7 +37,10 @@ void ISerializable.Deserialize(BinaryReader reader) void ISerializable.Serialize(BinaryWriter writer) { writer.Write((byte)Usage); - writer.WriteVarBytes(Data); + if (Usage == TransactionAttributeUsage.Cosigner) + writer.Write(Data); + else + writer.WriteVarBytes(Data); } public JObject ToJson() diff --git a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs index 89c1a1cec2..d878348c28 100644 --- a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs +++ b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs @@ -2,6 +2,7 @@ { public enum TransactionAttributeUsage : byte { + Cosigner = 0x20, Url = 0x81 } } diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 8af153119d..49dec1b4dc 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -15,48 +15,88 @@ namespace Neo.SmartContract { public class ContractParametersContext { + private class ContextItem + { + public byte[] Script; + public ContractParameter[] Parameters; + public Dictionary Signatures; + + private ContextItem() { } + + public ContextItem(Contract contract) + { + this.Script = contract.Script; + this.Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); + } + + public static ContextItem FromJson(JObject json) + { + return new ContextItem + { + Script = json["script"]?.AsString().HexToBytes(), + Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson(p)).ToArray(), + Signatures = json["signatures"]?.Properties.Select(p => new + { + PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), + Signature = p.Value.AsString().HexToBytes() + }).ToDictionary(p => p.PublicKey, p => p.Signature) + }; + } + + public JObject ToJson() + { + JObject json = new JObject(); + if (Script != null) + json["script"] = Script.ToHexString(); + json["parameters"] = new JArray(Parameters.Select(p => p.ToJson())); + if (Signatures != null) + { + json["signatures"] = new JObject(); + foreach (var signature in Signatures) + json["signatures"][signature.Key.ToString()] = signature.Value.ToHexString(); + } + return json; + } + } + public readonly IVerifiable Verifiable; - private byte[] Script; - private ContractParameter[] Parameters; - private Dictionary Signatures; + private readonly Dictionary ContextItems; public bool Completed { get { - if (Parameters is null) return false; - return Parameters.All(p => p.Value != null); + if (ContextItems.Count < ScriptHashes.Count) + return false; + return ContextItems.Values.All(p => p != null && p.Parameters.All(q => q.Value != null)); } } - private UInt160 _ScriptHash = null; - public UInt160 ScriptHash + private UInt160[] _ScriptHashes = null; + public IReadOnlyList ScriptHashes { get { - if (_ScriptHash == null) + if (_ScriptHashes == null) using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - _ScriptHash = Verifiable.GetScriptHashForVerification(snapshot); + _ScriptHashes = Verifiable.GetScriptHashesForVerifying(snapshot); } - return _ScriptHash; + return _ScriptHashes; } } public ContractParametersContext(IVerifiable verifiable) { this.Verifiable = verifiable; + this.ContextItems = new Dictionary(); } public bool Add(Contract contract, int index, object parameter) { - if (!ScriptHash.Equals(contract.ScriptHash)) return false; - if (Parameters is null) - { - Script = contract.Script; - Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); - } - Parameters[index].Value = parameter; + ContextItem item = CreateItem(contract); + if (item == null) return false; + item.Parameters[index].Value = parameter; return true; } @@ -64,16 +104,12 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { if (contract.Script.IsMultiSigContract()) { - if (!ScriptHash.Equals(contract.ScriptHash)) return false; - if (Parameters is null) - { - Script = contract.Script; - Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); - } - if (Parameters.All(p => p.Value != null)) return false; - if (Signatures == null) - Signatures = new Dictionary(); - else if (Signatures.ContainsKey(pubkey)) + ContextItem item = CreateItem(contract); + if (item == null) return false; + if (item.Parameters.All(p => p.Value != null)) return false; + if (item.Signatures == null) + item.Signatures = new Dictionary(); + else if (item.Signatures.ContainsKey(pubkey)) return false; List points = new List(); { @@ -94,15 +130,15 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) } } if (!points.Contains(pubkey)) return false; - Signatures.Add(pubkey, signature); - if (Signatures.Count == contract.ParameterList.Length) + item.Signatures.Add(pubkey, signature); + if (item.Signatures.Count == contract.ParameterList.Length) { Dictionary dic = points.Select((p, i) => new { PublicKey = p, Index = i }).ToDictionary(p => p.PublicKey, p => p.Index); - byte[][] sigs = Signatures.Select(p => new + byte[][] sigs = item.Signatures.Select(p => new { Signature = p.Value, Index = dic[p.Key] @@ -110,7 +146,7 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) for (int i = 0; i < sigs.Length; i++) if (!Add(contract, i, sigs[i])) throw new InvalidOperationException(); - Signatures = null; + item.Signatures = null; } return true; } @@ -134,52 +170,66 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) } } + private ContextItem CreateItem(Contract contract) + { + if (ContextItems.TryGetValue(contract.ScriptHash, out ContextItem item)) + return item; + if (!ScriptHashes.Contains(contract.ScriptHash)) + return null; + item = new ContextItem(contract); + ContextItems.Add(contract.ScriptHash, item); + return item; + } + public static ContractParametersContext FromJson(JObject json) { - IVerifiable verifiable = typeof(ContractParametersContext).GetTypeInfo().Assembly.CreateInstance(json["type"].AsString()) as IVerifiable; - if (verifiable == null) throw new FormatException(); + if (!(typeof(ContractParametersContext).GetTypeInfo().Assembly.CreateInstance(json["type"].AsString()) is IVerifiable verifiable)) throw new FormatException(); using (MemoryStream ms = new MemoryStream(json["hex"].AsString().HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { verifiable.DeserializeUnsigned(reader); } - return new ContractParametersContext(verifiable) + ContractParametersContext context = new ContractParametersContext(verifiable); + foreach (var property in json["items"].Properties) { - Script = json["script"]?.AsString().HexToBytes(), - Parameters = ((JArray)json["parameters"])?.Select(p => ContractParameter.FromJson(p)).ToArray(), - Signatures = json["signatures"]?.Properties.Select(p => new - { - PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), - Signature = p.Value.AsString().HexToBytes() - }).ToDictionary(p => p.PublicKey, p => p.Signature) - }; + context.ContextItems.Add(UInt160.Parse(property.Key), ContextItem.FromJson(property.Value)); + } + return context; } - public ContractParameter GetParameter(int index) + public ContractParameter GetParameter(UInt160 scriptHash, int index) { - return GetParameters()?[index]; + return GetParameters(scriptHash)?[index]; } - public IReadOnlyList GetParameters() + public IReadOnlyList GetParameters(UInt160 scriptHash) { - return Parameters; + if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) + return null; + return item.Parameters; } - public Witness GetWitness() + public Witness[] GetWitnesses() { if (!Completed) throw new InvalidOperationException(); - using (ScriptBuilder sb = new ScriptBuilder()) + Witness[] witnesses = new Witness[ScriptHashes.Count]; + for (int i = 0; i < ScriptHashes.Count; i++) { - foreach (ContractParameter parameter in Parameters.Reverse()) + ContextItem item = ContextItems[ScriptHashes[i]]; + using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitPush(parameter); + foreach (ContractParameter parameter in item.Parameters.Reverse()) + { + sb.EmitPush(parameter); + } + witnesses[i] = new Witness + { + InvocationScript = sb.ToArray(), + VerificationScript = item.Script ?? new byte[0] + }; } - return new Witness - { - InvocationScript = sb.ToArray(), - VerificationScript = Script ?? new byte[0] - }; } + return witnesses; } public static ContractParametersContext Parse(string value) @@ -198,16 +248,9 @@ public JObject ToJson() writer.Flush(); json["hex"] = ms.ToArray().ToHexString(); } - if (Script != null) - json["script"] = Script.ToHexString(); - if (Parameters != null) - json["parameters"] = new JArray(Parameters.Select(p => p.ToJson())); - if (Signatures != null) - { - json["signatures"] = new JObject(); - foreach (var signature in Signatures) - json["signatures"][signature.Key.ToString()] = signature.Value.ToHexString(); - } + json["items"] = new JObject(); + foreach (var item in ContextItems) + json["items"][item.Key.ToString()] = item.Value.ToJson(); return json; } @@ -216,4 +259,4 @@ public override string ToString() return ToJson().ToString(); } } -} +} \ No newline at end of file diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 9c366bb426..62e780f2c0 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -246,35 +246,39 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Default.Hash160(script)); } - internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long gas) + internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snapshot, long gas) { if (gas < 0) return false; - UInt160 hash; + UInt160[] hashes; try { - hash = verifiable.GetScriptHashForVerification(snapshot); + hashes = verifiable.GetScriptHashesForVerifying(snapshot); } catch (InvalidOperationException) { return false; } - byte[] verification = verifiable.Witness.VerificationScript; - if (verification.Length == 0) + if (hashes.Length != verifiable.Witnesses.Length) return false; + for (int i = 0; i < hashes.Length; i++) { - verification = snapshot.Contracts.TryGet(hash)?.Script; - if (verification is null) return false; - } - else - { - if (hash != verifiable.Witness.ScriptHash) return false; - } - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) - { - engine.LoadScript(verification); - engine.LoadScript(verifiable.Witness.InvocationScript); - if (engine.Execute().HasFlag(VMState.FAULT)) return false; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; + byte[] verification = verifiable.Witnesses[i].VerificationScript; + if (verification.Length == 0) + { + verification = snapshot.Contracts.TryGet(hashes[i])?.Script; + if (verification is null) return false; + } + else + { + if (hashes[i] != verifiable.Witnesses[i].ScriptHash) return false; + } + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) + { + engine.LoadScript(verification); + engine.LoadScript(verifiable.Witnesses[i].InvocationScript); + if (engine.Execute().HasFlag(VMState.FAULT)) return false; + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; + } } return true; } diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index d1e1d50aa3..005df38439 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -212,10 +212,16 @@ private static bool Transaction_GetWitnessScript(ApplicationEngine engine) { Transaction tx = _interface.GetInterface(); if (tx == null) return false; - byte[] script = tx.Witness.VerificationScript; - if (script.Length == 0) - script = engine.Snapshot.Contracts[tx.Sender].Script; - engine.CurrentContext.EvaluationStack.Push(script); + + var array = new VMArray(); + foreach (var witness in tx.Witnesses) + { + byte[] script = witness.VerificationScript; + if (script.Length == 0) + script = engine.Snapshot.Contracts[tx.Sender].Script; + array.Add(script); + } + engine.CurrentContext.EvaluationStack.Push(array); return true; } return false; diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index b110fe7796..074b5c6cb7 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -146,7 +146,8 @@ private static bool Runtime_GetTrigger(ApplicationEngine engine) internal static bool CheckWitness(ApplicationEngine engine, UInt160 hash) { - return hash.Equals(engine.ScriptContainer.GetScriptHashForVerification(engine.Snapshot)); + var _hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); + return _hashes_for_verifying.Contains(hash); } private static bool CheckWitness(ApplicationEngine engine, ECPoint pubkey) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index a7578651d0..62dfbb37c0 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -248,11 +248,16 @@ private Transaction MakeTransaction(Snapshot snapshot, byte version, uint nonce, public bool Sign(ContractParametersContext context) { - WalletAccount account = GetAccount(context.ScriptHash); - if (account?.HasKey != true) return false; - KeyPair key = account.GetKey(); - byte[] signature = context.Verifiable.Sign(key); - return context.AddSignature(account.Contract, key.PublicKey, signature); + bool fSuccess = false; + foreach (UInt160 scriptHash in context.ScriptHashes) + { + WalletAccount account = GetAccount(scriptHash); + if (account?.HasKey != true) continue; + KeyPair key = account.GetKey(); + byte[] signature = context.Verifiable.Sign(key); + fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); + } + return fSuccess; } public abstract bool VerifyPassword(string password); From 777b4793d5f5021ed90b72196bdc584adab52958 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Mon, 10 Jun 2019 17:14:01 +0800 Subject: [PATCH 11/46] some fixes --- .../Nep5NativeContractExtensions.cs | 6 +++- neo.UnitTests/TestVerifiable.cs | 6 +++- neo.UnitTests/UT_Block.cs | 16 +++++------ neo.UnitTests/UT_Consensus.cs | 2 +- neo.UnitTests/UT_Header.cs | 8 +++--- neo/Cryptography/Helper.cs | 3 +- neo/Network/P2P/Payloads/BlockBase.cs | 28 +++++++++++++------ neo/Network/P2P/Payloads/ConsensusPayload.cs | 23 +++++++++++---- neo/Network/P2P/Payloads/IVerifiable.cs | 2 +- neo/Network/P2P/Payloads/Transaction.cs | 7 +++-- .../P2P/Payloads/TransactionAttribute.cs | 21 ++------------ neo/SmartContract/InteropService.NEO.cs | 28 +++++++++++-------- neo/SmartContract/WitnessWrapper.cs | 27 ++++++++++++++++++ 13 files changed, 114 insertions(+), 63 deletions(-) create mode 100644 neo/SmartContract/WitnessWrapper.cs diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index 024f7646c0..a87fd7c3e1 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -17,7 +17,11 @@ internal class ManualWitness : IVerifiable { private readonly UInt160[] _hashForVerify; - public Witness[] Witnesses => throw new NotImplementedException(); + public Witness[] Witnesses + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } public int Size => 0; diff --git a/neo.UnitTests/TestVerifiable.cs b/neo.UnitTests/TestVerifiable.cs index dc37debb32..dfe21c2d0f 100644 --- a/neo.UnitTests/TestVerifiable.cs +++ b/neo.UnitTests/TestVerifiable.cs @@ -9,7 +9,11 @@ public class TestVerifiable : IVerifiable { private readonly string testStr = "testStr"; - public Witness[] Witnesses { get => throw new NotImplementedException(); } + public Witness[] Witnesses + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } public int Size => throw new NotImplementedException(); diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index dbabe1093e..f4e73decc0 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -43,9 +43,9 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); - // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 3 + // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 4 // block 9 + 1 - uut.Size.Should().Be(109); + uut.Size.Should().Be(110); } [TestMethod] @@ -59,7 +59,7 @@ public void Size_Get_1_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(160); + uut.Size.Should().Be(161); } [TestMethod] @@ -75,7 +75,7 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(262); + uut.Size.Should().Be(263); } [TestMethod] @@ -94,7 +94,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; + byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) @@ -111,7 +111,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -215,7 +215,7 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); jObj["hash"].AsString().Should().Be("0xe0b482b6e4c176c9af520dd7caa6d80a9aeaeb80d016e788c702b05f5ac5ba4b"); - jObj["size"].AsNumber().Should().Be(160); + jObj["size"].AsNumber().Should().Be(161); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); jObj["merkleroot"].AsString().Should().Be("0xc59eab3a287f74405b80325bc26c66ac7ef18d94c434a18d60950d3f098280f2"); @@ -223,7 +223,7 @@ public void ToJson() jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); - JObject scObj = jObj["witness"]; + JObject scObj = ((JArray)jObj["witnesses"])[0]; scObj["invocation"].AsString().Should().Be(""); scObj["verification"].AsString().Should().Be("51"); diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index 4ea314238b..d410bb5142 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -75,7 +75,7 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() // Creating proposed block Header header = new Header(); TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal); - header.Size.Should().Be(100); + header.Size.Should().Be(101); Console.WriteLine($"header {header} hash {header.Hash} timstamp {timestampVal}"); diff --git a/neo.UnitTests/UT_Header.cs b/neo.UnitTests/UT_Header.cs index e3df13e14d..5cf0f84764 100644 --- a/neo.UnitTests/UT_Header.cs +++ b/neo.UnitTests/UT_Header.cs @@ -22,9 +22,9 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 3 + // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 4 // header 1 - uut.Size.Should().Be(100); + uut.Size.Should().Be(101); } [TestMethod] @@ -35,7 +35,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 81, 0 }; + byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -106,7 +106,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 81, 0 }; + byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) diff --git a/neo/Cryptography/Helper.cs b/neo/Cryptography/Helper.cs index f46aad0121..48410049f1 100644 --- a/neo/Cryptography/Helper.cs +++ b/neo/Cryptography/Helper.cs @@ -117,7 +117,8 @@ public static byte[] Sha256(this byte[] value, int offset, int count) internal static bool Test(this BloomFilter filter, Transaction tx) { if (filter.Check(tx.Hash.ToArray())) return true; - if (filter.Check(tx.Sender.ToArray())) return true; + if (tx.Witnesses.Any(p => filter.Check(p.ScriptHash.ToArray()))) + return true; return false; } diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 7e6e1c7e10..9f56ca0d97 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -17,9 +17,7 @@ public abstract class BlockBase : IVerifiable public uint Timestamp; public uint Index; public UInt160 NextConsensus; - public Witness Witness { get; set; } - - Witness[] IVerifiable.Witnesses => new[] { Witness }; + public Witness Witness; private UInt256 _hash = null; public UInt256 Hash @@ -34,11 +32,25 @@ public UInt256 Hash } } - public virtual int Size => sizeof(uint) + PrevHash.Size + MerkleRoot.Size + sizeof(uint) + sizeof(uint) + NextConsensus.Size + Witness.Size; + public virtual int Size => sizeof(uint) + PrevHash.Size + MerkleRoot.Size + sizeof(uint) + sizeof(uint) + NextConsensus.Size + 1 + Witness.Size; + + Witness[] IVerifiable.Witnesses + { + get + { + return new[] { Witness }; + } + set + { + if (value.Length != 1) throw new ArgumentException(); + Witness = value[0]; + } + } public virtual void Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); + if (reader.ReadByte() != 1) throw new FormatException(); Witness = reader.ReadSerializable(); } @@ -54,16 +66,16 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) { - if (PrevHash == UInt256.Zero) return new UInt160[] { Witness.ScriptHash }; + if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; Header prev_header = snapshot.GetHeader(PrevHash); if (prev_header == null) throw new InvalidOperationException(); - return new UInt160[] { prev_header.NextConsensus }; + return new[] { prev_header.NextConsensus }; } public virtual void Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witness); + writer.Write((byte)1); writer.Write(Witness); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) @@ -87,7 +99,7 @@ public virtual JObject ToJson() json["time"] = Timestamp; json["index"] = Index; json["nextconsensus"] = NextConsensus.ToAddress(); - json["witness"] = Witness.ToJson(); + json["witnesses"] = new JArray(Witness.ToJson()); return json; } diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index 122c92ab76..7a20e7c46b 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -17,8 +17,7 @@ public class ConsensusPayload : IInventory public uint BlockIndex; public ushort ValidatorIndex; public byte[] Data; - public Witness Witness { get; set; } - Witness[] IVerifiable.Witnesses => new[] { Witness }; + public Witness Witness; private ConsensusMessage _deserializedMessage = null; public ConsensusMessage ConsensusMessage @@ -61,7 +60,20 @@ public UInt256 Hash sizeof(ushort) + //ValidatorIndex sizeof(uint) + //Timestamp Data.GetVarSize() + //Data - Witness.Size; //Witness + 1 + Witness.Size; //Witness + + Witness[] IVerifiable.Witnesses + { + get + { + return new[] { Witness }; + } + set + { + if (value.Length != 1) throw new ArgumentException(); + Witness = value[0]; + } + } public T GetDeserializedMessage() where T : ConsensusMessage { @@ -71,6 +83,7 @@ public T GetDeserializedMessage() where T : ConsensusMessage void ISerializable.Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); + if (reader.ReadByte() != 1) throw new FormatException(); Witness = reader.ReadSerializable(); } @@ -88,13 +101,13 @@ UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) ECPoint[] validators = NativeContract.NEO.GetNextBlockValidators(snapshot); if (validators.Length <= ValidatorIndex) throw new InvalidOperationException(); - return new UInt160[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; + return new[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; } void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witness); + writer.Write((byte)1); writer.Write(Witness); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) diff --git a/neo/Network/P2P/Payloads/IVerifiable.cs b/neo/Network/P2P/Payloads/IVerifiable.cs index 26ce90574c..50651ad9ff 100644 --- a/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/neo/Network/P2P/Payloads/IVerifiable.cs @@ -6,7 +6,7 @@ namespace Neo.Network.P2P.Payloads { public interface IVerifiable : ISerializable { - Witness[] Witnesses { get; } + Witness[] Witnesses { get; set; } void DeserializeUnsigned(BinaryReader reader); diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 4688d1c3ae..f2e1fc567b 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -105,7 +105,8 @@ public void CalculateFees() { // Compute verification gas - foreach(var witness in Witnesses) + consumed = 0; + foreach (var witness in Witnesses) { byte[] verification = witness.VerificationScript; if (verification.Length == 0) @@ -124,7 +125,7 @@ public void CalculateFees() engine.LoadScript(witness.InvocationScript); if (engine.Execute().HasFlag(VMState.FAULT)) return; if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return; - consumed = engine.GasConsumed; + consumed += engine.GasConsumed; } } @@ -159,7 +160,7 @@ public void DeserializeUnsigned(BinaryReader reader) if (Gas + NetworkFee < Gas) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); - var cosigners = GetScriptHashesForVerifying(null); + var cosigners = Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data)).ToArray(); if (cosigners.Distinct().Count() != cosigners.Length) throw new FormatException(); } diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/neo/Network/P2P/Payloads/TransactionAttribute.cs index 810f497ddd..2f384fa046 100644 --- a/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -10,25 +10,11 @@ public class TransactionAttribute : ISerializable public TransactionAttributeUsage Usage; public byte[] Data; - public int Size - { - get - { - if (Usage == TransactionAttributeUsage.Cosigner) - return sizeof(TransactionAttributeUsage) + 20; - else - return sizeof(TransactionAttributeUsage) + Data.GetVarSize(); - } - } + public int Size => sizeof(TransactionAttributeUsage) + Data.GetVarSize(); void ISerializable.Deserialize(BinaryReader reader) { Usage = (TransactionAttributeUsage)reader.ReadByte(); - if (Usage == TransactionAttributeUsage.Cosigner) - Data = reader.ReadBytes(20); - else if (Usage == TransactionAttributeUsage.Url) - Data = reader.ReadVarBytes(252); - else if (!Enum.IsDefined(typeof(TransactionAttributeUsage), Usage)) throw new FormatException(); Data = reader.ReadVarBytes(252); @@ -37,10 +23,7 @@ void ISerializable.Deserialize(BinaryReader reader) void ISerializable.Serialize(BinaryWriter writer) { writer.Write((byte)Usage); - if (Usage == TransactionAttributeUsage.Cosigner) - writer.Write(Data); - else - writer.WriteVarBytes(Data); + writer.WriteVarBytes(Data); } public JObject ToJson() diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 005df38439..a496497962 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -25,7 +25,8 @@ static partial class InteropService public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400); public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400); public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400); - public static readonly uint Neo_Transaction_GetWitnessScript = Register("Neo.Transaction.GetWitnessScript", Transaction_GetWitnessScript, 0_00000400); + public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000); + public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400); public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000); public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice); public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice); @@ -206,22 +207,27 @@ private static bool Transaction_GetScript(ApplicationEngine engine) return false; } - private static bool Transaction_GetWitnessScript(ApplicationEngine engine) + private static bool Transaction_GetWitnesses(ApplicationEngine engine) { if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { Transaction tx = _interface.GetInterface(); if (tx == null) return false; + if (tx.Witnesses.Length > engine.MaxArraySize) + return false; + engine.CurrentContext.EvaluationStack.Push(WitnessWrapper.Create(tx, engine.Snapshot).Select(p => StackItem.FromInterface(p)).ToArray()); + return true; + } + return false; + } - var array = new VMArray(); - foreach (var witness in tx.Witnesses) - { - byte[] script = witness.VerificationScript; - if (script.Length == 0) - script = engine.Snapshot.Contracts[tx.Sender].Script; - array.Add(script); - } - engine.CurrentContext.EvaluationStack.Push(array); + private static bool Witness_GetVerificationScript(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + WitnessWrapper witness = _interface.GetInterface(); + if (witness == null) return false; + engine.CurrentContext.EvaluationStack.Push(witness.VerificationScript); return true; } return false; diff --git a/neo/SmartContract/WitnessWrapper.cs b/neo/SmartContract/WitnessWrapper.cs new file mode 100644 index 0000000000..f67690cad5 --- /dev/null +++ b/neo/SmartContract/WitnessWrapper.cs @@ -0,0 +1,27 @@ +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using System.Linq; + +namespace Neo.SmartContract +{ + internal class WitnessWrapper + { + public byte[] VerificationScript; + + public static WitnessWrapper[] Create(IVerifiable verifiable, Snapshot snapshot) + { + WitnessWrapper[] wrappers = verifiable.Witnesses.Select(p => new WitnessWrapper + { + VerificationScript = p.VerificationScript + }).ToArray(); + if (wrappers.Any(p => p.VerificationScript.Length == 0)) + { + UInt160[] hashes = verifiable.GetScriptHashesForVerifying(snapshot); + for (int i = 0; i < wrappers.Length; i++) + if (wrappers[i].VerificationScript.Length == 0) + wrappers[i].VerificationScript = snapshot.Contracts[hashes[i]].Script; + } + return wrappers; + } + } +} From 7dda436117cd2a4359e69b56cbe94d912a6e6f0c Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 13 Jun 2019 09:32:53 +0200 Subject: [PATCH 12/46] Update Transaction.cs --- neo/Network/P2P/Payloads/Transaction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index f2e1fc567b..ec827b4f50 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -240,7 +240,7 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - var fee = Gas + NetworkFee; + BigInteger fee = Gas + NetworkFee; if (balance < fee) return false; fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); if (balance < fee) return false; From 838a72bb59e47bfebe59ed784bf36e44652c3dc4 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 13 Jun 2019 15:37:09 +0800 Subject: [PATCH 13/46] format --- neo/Ledger/Blockchain.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 83261a8634..9aad109789 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -154,11 +154,14 @@ private static Transaction DeployNativeContracts() Sender = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(), Gas = 0, Attributes = new TransactionAttribute[0], - Witnesses = new Witness[]{ new Witness + Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new[] { (byte)OpCode.PUSHT } - } } + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new[] { (byte)OpCode.PUSHT } + } + } }; } From 57a70b8db9d08049e17338343e6c627384621e88 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 13 Jun 2019 15:49:17 +0800 Subject: [PATCH 14/46] fix overflow --- neo/Network/P2P/Payloads/Transaction.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index ec827b4f50..b1e35b36e1 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -234,17 +234,16 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) return false; int size = Size; if (size > MaxTransactionSize) return false; - var feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); - if (NetworkFee / size < feePerByte) - return false; + long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); + if (net_fee < 0) return false; if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); BigInteger fee = Gas + NetworkFee; if (balance < fee) return false; - fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); + fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.Gas + p.NetworkFee)).Sum(); if (balance < fee) return false; - return this.VerifyWitnesses(snapshot, NetworkFee - (size * feePerByte)); + return this.VerifyWitnesses(snapshot, net_fee); } } } From 43e61e7172995e7c90b8cd9df7e1c21bfd0fa73a Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sat, 15 Jun 2019 18:16:58 +0800 Subject: [PATCH 15/46] fix `Wallet.MakeTransaction()` --- neo/Wallets/Wallet.cs | 197 +++++++++++++++++++++++++++--------------- 1 file changed, 129 insertions(+), 68 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 62dfbb37c0..c8c7c8f382 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -18,8 +18,6 @@ namespace Neo.Wallets { public abstract class Wallet { - private static readonly Random rand = new Random(); - public abstract string Name { get; } public abstract Version Version { get; } @@ -49,27 +47,62 @@ public WalletAccount CreateAccount(Contract contract, byte[] privateKey) return CreateAccount(contract, new KeyPair(privateKey)); } - public void FillTransaction(Transaction tx, UInt160 sender = null) + private List<(UInt160 Account, BigInteger Value)> FindPayingAccounts(List<(UInt160 Account, BigInteger Value)> orderedAccounts, BigInteger amount) { - if (tx.Nonce == 0) - tx.Nonce = (uint)rand.Next(); - if (tx.ValidUntilBlock == 0) - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - tx.ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement; - tx.CalculateFees(); - UInt160[] accounts = sender is null ? GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).ToArray() : new[] { sender }; - BigInteger fee = tx.Gas + tx.NetworkFee; - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - foreach (UInt160 account in accounts) + var result = new List<(UInt160 Account, BigInteger Value)>(); + BigInteger sum_balance = orderedAccounts.Select(p => p.Value).Sum(); + if (sum_balance == amount) + { + result.AddRange(orderedAccounts); + orderedAccounts.Clear(); + } + else + { + for (int i = 0; i < orderedAccounts.Count; i++) { - BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, account); - if (balance >= fee) + if (orderedAccounts[i].Value < amount) + continue; + if (orderedAccounts[i].Value == amount) + { + result.Add(orderedAccounts[i]); + orderedAccounts.RemoveAt(i); + } + else { - tx.Sender = account; - return; + result.Add((orderedAccounts[i].Account, amount)); + orderedAccounts[i] = (orderedAccounts[i].Account, orderedAccounts[i].Value - amount); } + break; } - throw new InvalidOperationException(); + if (result.Count == 0) + { + int i = orderedAccounts.Count - 1; + while (orderedAccounts[i].Value <= amount) + { + result.Add(orderedAccounts[i]); + amount -= orderedAccounts[i].Value; + orderedAccounts.RemoveAt(i); + i--; + } + for (i = 0; i < orderedAccounts.Count; i++) + { + if (orderedAccounts[i].Value < amount) + continue; + if (orderedAccounts[i].Value == amount) + { + result.Add(orderedAccounts[i]); + orderedAccounts.RemoveAt(i); + } + else + { + result.Add((orderedAccounts[i].Account, amount)); + orderedAccounts[i] = (orderedAccounts[i].Account, orderedAccounts[i].Value - amount); + } + break; + } + } + } + return result; } public WalletAccount GetAccount(ECPoint pubkey) @@ -168,10 +201,8 @@ public virtual WalletAccount Import(string nep2, string passphrase) return account; } - public Transaction MakeTransaction(IEnumerable attributes, TransferOutput[] outputs, UInt160 from = null) + public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null) { - uint nonce = (uint)rand.Next(); - var totalPay = outputs.GroupBy(p => p.AssetId, (k, g) => (k, g.Select(p => p.Value.Value).Sum())).ToArray(); UInt160[] accounts; if (from is null) { @@ -182,68 +213,98 @@ public Transaction MakeTransaction(IEnumerable attributes, if (!Contains(from)) return null; accounts = new[] { from }; } - TransactionAttribute[] attr = attributes?.ToArray() ?? new TransactionAttribute[0]; using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - uint validUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement; - foreach (UInt160 account in accounts) + HashSet cosigners = new HashSet(); + byte[] script; + List<(UInt160 Account, BigInteger Value)> balances_gas = null; + using (ScriptBuilder sb = new ScriptBuilder()) { - Transaction tx = MakeTransaction(snapshot, 0, nonce, totalPay, outputs, account, validUntilBlock, attr); - if (tx != null) return tx; + foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) + { + var balances = new List<(UInt160 Account, BigInteger Value)>(); + foreach (UInt160 account in accounts) + using (ScriptBuilder sb2 = new ScriptBuilder()) + { + sb2.EmitAppCall(assetId, "balanceOf", account); + ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot); + if (engine.State.HasFlag(VMState.FAULT)) return null; + BigInteger value = engine.ResultStack.Pop().GetBigInteger(); + if (value.Sign > 0) balances.Add((account, value)); + } + BigInteger sum_balance = balances.Select(p => p.Value).Sum(); + if (sum_balance < sum) return null; + foreach (TransferOutput output in group) + { + balances = balances.OrderBy(p => p.Value).ToList(); + var balances_used = FindPayingAccounts(balances, output.Value.Value); + cosigners.UnionWith(balances_used.Select(p => p.Account)); + foreach (var (account, value) in balances_used) + { + sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); + sb.Emit(OpCode.THROWIFNOT); + } + } + if (assetId.Equals(NativeContract.GAS.Hash)) + balances_gas = balances; + } + script = sb.ToArray(); } + if (balances_gas is null) + balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } - return null; } - private Transaction MakeTransaction(Snapshot snapshot, byte version, uint nonce, (UInt160, BigInteger)[] totalPay, TransferOutput[] outputs, UInt160 sender, uint validUntilBlock, TransactionAttribute[] attributes) + public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) { - BigInteger balance_gas = BigInteger.Zero; - foreach (var (assetId, amount) in totalPay) - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitAppCall(assetId, "balanceOf", sender); - ApplicationEngine engine = ApplicationEngine.Run(sb.ToArray()); - if (engine.State.HasFlag(VMState.FAULT)) return null; - BigInteger balance = engine.ResultStack.Peek().GetBigInteger(); - if (balance < amount) return null; - if (assetId.Equals(NativeContract.GAS.Hash)) - { - balance_gas = balance - amount; - if (balance_gas.Sign <= 0) return null; - } - } - byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) + UInt160[] accounts; + if (sender is null) { - foreach (var output in outputs) - { - sb.EmitAppCall(output.AssetId, "transfer", sender, output.ScriptHash, output.Value.Value); - sb.Emit(OpCode.THROWIFNOT); - } - script = sb.ToArray(); + accounts = GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).ToArray(); } - Transaction tx = new Transaction + else { - Version = version, - Nonce = nonce, - Script = script, - Sender = sender, - ValidUntilBlock = validUntilBlock, - Attributes = attributes - }; - try + if (!Contains(sender)) return null; + accounts = new[] { sender }; + } + using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - tx.CalculateFees(); + var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } - catch (InvalidOperationException) + } + + private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) + { + Random rand = new Random(); + foreach (var (account, value) in balances_gas) { - return null; + Transaction tx = new Transaction + { + Version = 0, + Nonce = (uint)rand.Next(), + Script = script, + Sender = account, + ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, + Attributes = attributes + }; + ContractParametersContext context = new ContractParametersContext(tx); + if (!Sign(context)) continue; + if (!context.Completed) continue; + tx.Witnesses = context.GetWitnesses(); + try + { + tx.CalculateFees(); + } + catch (InvalidOperationException) + { + continue; + } + if (value >= tx.Gas + tx.NetworkFee) return tx; } - BigInteger fee = tx.Gas + tx.NetworkFee; - if (balance_gas == BigInteger.Zero) - balance_gas = NativeContract.GAS.BalanceOf(snapshot, sender); - if (balance_gas < fee) return null; - return tx; + return null; } public bool Sign(ContractParametersContext context) From 206876e48cbef4c6f6110b31ac0a5b71feb0b42a Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sat, 15 Jun 2019 18:22:02 +0800 Subject: [PATCH 16/46] fix `Transaction.CalculateFees()` --- neo/Network/P2P/Payloads/Transaction.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index b1e35b36e1..4055af9d29 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -95,10 +95,9 @@ public void CalculateFees() else { long remainder = Gas % d; - if (remainder == 0) return; if (remainder > 0) Gas += d - remainder; - else + else if (remainder < 0) Gas -= remainder; } using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) @@ -112,19 +111,16 @@ public void CalculateFees() if (verification.Length == 0) { verification = snapshot.Contracts.TryGet(Sender)?.Script; - if (verification is null) return; - } - else - { - if (Sender != witness.ScriptHash) return; + if (verification is null) throw new InvalidOperationException(); } using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) { engine.LoadScript(verification); engine.LoadScript(witness.InvocationScript); - if (engine.Execute().HasFlag(VMState.FAULT)) return; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return; + if (engine.Execute().HasFlag(VMState.FAULT)) throw new InvalidOperationException(); + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) + throw new InvalidOperationException(); consumed += engine.GasConsumed; } } From 6d0dad25ce09a1e25bf47ef2944bb213cbea6f42 Mon Sep 17 00:00:00 2001 From: Igor Machado Date: Sat, 15 Jun 2019 18:49:46 -0300 Subject: [PATCH 17/46] remove contract get from verification --- neo/Network/P2P/Payloads/Transaction.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 4055af9d29..c284352d3f 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -108,11 +108,7 @@ public void CalculateFees() foreach (var witness in Witnesses) { byte[] verification = witness.VerificationScript; - if (verification.Length == 0) - { - verification = snapshot.Contracts.TryGet(Sender)?.Script; - if (verification is null) throw new InvalidOperationException(); - } + if (verification is null) throw new InvalidOperationException(); using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) { From 37c668da955285e70822b79a381213781118771c Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 16 Jun 2019 12:36:42 +0800 Subject: [PATCH 18/46] Revert "remove contract get from verification" This reverts commit 6d0dad25ce09a1e25bf47ef2944bb213cbea6f42. --- neo/Network/P2P/Payloads/Transaction.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index c284352d3f..4055af9d29 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -108,7 +108,11 @@ public void CalculateFees() foreach (var witness in Witnesses) { byte[] verification = witness.VerificationScript; - if (verification is null) throw new InvalidOperationException(); + if (verification.Length == 0) + { + verification = snapshot.Contracts.TryGet(Sender)?.Script; + if (verification is null) throw new InvalidOperationException(); + } using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) { From d10b45235a305313ac06b94519c3cbe6c145d871 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 19 Jun 2019 16:42:31 +0800 Subject: [PATCH 19/46] Fix fee calculation --- neo/Network/P2P/Payloads/Transaction.cs | 92 ++++--------------------- neo/SmartContract/Helper.cs | 6 +- neo/Wallets/Wallet.cs | 48 ++++++++++--- 3 files changed, 58 insertions(+), 88 deletions(-) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 4055af9d29..87b0c8fc1f 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -1,11 +1,9 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; using Neo.Wallets; using System; using System.Collections.Generic; @@ -26,7 +24,6 @@ public class Transaction : IEquatable, IInventory public byte Version; public uint Nonce; - public byte[] Script; public UInt160 Sender; /// /// Distributed to NEO holders. @@ -38,6 +35,7 @@ public class Transaction : IEquatable, IInventory public long NetworkFee; public uint ValidUntilBlock; public TransactionAttribute[] Attributes; + public byte[] Script; public Witness[] Witnesses { get; set; } /// @@ -61,79 +59,19 @@ public UInt256 Hash InventoryType IInventory.InventoryType => InventoryType.TX; - public int Size => - sizeof(byte) + //Version - sizeof(uint) + //Nonce - Script.GetVarSize() + //Script - Sender.Size + //Sender - sizeof(long) + //Gas - sizeof(long) + //NetworkFee - sizeof(uint) + //ValidUntilBlock + public const int HeaderSize = + sizeof(byte) + //Version + sizeof(uint) + //Nonce + 20 + //Sender + sizeof(long) + //Gas + sizeof(long) + //NetworkFee + sizeof(uint); //ValidUntilBlock + + public int Size => HeaderSize + Attributes.GetVarSize() + //Attributes + Script.GetVarSize() + //Script Witnesses.GetVarSize(); //Witnesses - public void CalculateFees() - { - if (Sender is null) Sender = UInt160.Zero; - if (Attributes is null) Attributes = new TransactionAttribute[0]; - if (Witnesses is null) Witnesses = new Witness[0]; - _hash = null; - long consumed; - using (ApplicationEngine engine = ApplicationEngine.Run(Script, this, testMode: true)) - { - if (engine.State.HasFlag(VMState.FAULT)) - throw new InvalidOperationException(); - consumed = engine.GasConsumed; - } - _hash = null; - long d = (long)NativeContract.GAS.Factor; - Gas = consumed - ApplicationEngine.GasFree; - if (Gas <= 0) - { - Gas = 0; - } - else - { - long remainder = Gas % d; - if (remainder > 0) - Gas += d - remainder; - else if (remainder < 0) - Gas -= remainder; - } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - { - // Compute verification gas - - consumed = 0; - foreach (var witness in Witnesses) - { - byte[] verification = witness.VerificationScript; - if (verification.Length == 0) - { - verification = snapshot.Contracts.TryGet(Sender)?.Script; - if (verification is null) throw new InvalidOperationException(); - } - - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, this, snapshot, 0, true)) - { - engine.LoadScript(verification); - engine.LoadScript(witness.InvocationScript); - if (engine.Execute().HasFlag(VMState.FAULT)) throw new InvalidOperationException(); - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) - throw new InvalidOperationException(); - consumed += engine.GasConsumed; - } - } - - // Compute fee per byte - - long feeperbyte = NativeContract.Policy.GetFeePerByte(snapshot); - long fee = consumed + (feeperbyte * Size); - if (fee > NetworkFee) - NetworkFee = fee; - } - } - void ISerializable.Deserialize(BinaryReader reader) { DeserializeUnsigned(reader); @@ -145,8 +83,6 @@ public void DeserializeUnsigned(BinaryReader reader) Version = reader.ReadByte(); if (Version > 0) throw new FormatException(); Nonce = reader.ReadUInt32(); - Script = reader.ReadVarBytes(ushort.MaxValue); - if (Script.Length == 0) throw new FormatException(); Sender = reader.ReadSerializable(); Gas = reader.ReadInt64(); if (Gas < 0) throw new FormatException(); @@ -158,6 +94,8 @@ public void DeserializeUnsigned(BinaryReader reader) Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); var cosigners = Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data)).ToArray(); if (cosigners.Distinct().Count() != cosigners.Length) throw new FormatException(); + Script = reader.ReadVarBytes(ushort.MaxValue); + if (Script.Length == 0) throw new FormatException(); } public bool Equals(Transaction other) @@ -194,12 +132,12 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) { writer.Write(Version); writer.Write(Nonce); - writer.WriteVarBytes(Script); writer.Write(Sender); writer.Write(Gas); writer.Write(NetworkFee); writer.Write(ValidUntilBlock); writer.Write(Attributes); + writer.WriteVarBytes(Script); } public JObject ToJson() @@ -209,12 +147,12 @@ public JObject ToJson() json["size"] = Size; json["version"] = Version; json["nonce"] = Nonce; - json["script"] = Script.ToHexString(); json["sender"] = Sender.ToAddress(); json["gas"] = new BigDecimal(Gas, (byte)NativeContract.GAS.Decimals).ToString(); json["net_fee"] = new BigDecimal(NetworkFee, (byte)NativeContract.GAS.Decimals).ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); + json["script"] = Script.ToHexString(); json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; } diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 62e780f2c0..f40dad212e 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -108,9 +108,9 @@ private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArray return stack_temp.Peek(); } - public static bool IsMultiSigContract(this byte[] script) + public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { - int m, n = 0; + m = 0; n = 0; int i = 0; if (script.Length < 41) return false; if (script[i] > (byte)OpCode.PUSH16) return false; @@ -170,7 +170,7 @@ public static bool IsSignatureContract(this byte[] script) public static bool IsStandardContract(this byte[] script) { - return script.IsSignatureContract() || script.IsMultiSigContract(); + return script.IsSignatureContract() || script.IsMultiSigContract(out _, out _); } public static byte[] Serialize(this StackItem item) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index c8c7c8f382..05843ef8be 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -1,4 +1,5 @@ using Neo.Cryptography; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -290,18 +291,49 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, Attributes = attributes }; - ContractParametersContext context = new ContractParametersContext(tx); - if (!Sign(context)) continue; - if (!context.Completed) continue; - tx.Witnesses = context.GetWitnesses(); - try + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, tx, testMode: true)) { - tx.CalculateFees(); + if (engine.State.HasFlag(VMState.FAULT)) return null; + tx.Gas = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); + if (tx.Gas > 0) + { + long d = (long)NativeContract.GAS.Factor; + long remainder = tx.Gas % d; + if (remainder > 0) + tx.Gas += d - remainder; + else if (remainder < 0) + tx.Gas -= remainder; + } } - catch (InvalidOperationException) + UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); + int size = Transaction.HeaderSize + attributes.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + foreach (UInt160 hash in hashes) { - continue; + script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; + if (script is null) continue; + if (script.IsSignatureContract()) + { + size += 66 + script.GetVarSize(); + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + } + else if (script.IsMultiSigContract(out int m, out int n)) + { + int size_inv = 65 * m; + size += IO.Helper.GetVarSize(size_inv) + size_inv + script.GetVarSize(); + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; + using (ScriptBuilder sb = new ScriptBuilder()) + tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; + using (ScriptBuilder sb = new ScriptBuilder()) + tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; + tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; + } + else + { + //We can support more contract types in the future. + } } + tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); if (value >= tx.Gas + tx.NetworkFee) return tx; } return null; From 9e9dafb42a759b7642b51ed2c7564e0f577d5c39 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 19 Jun 2019 16:45:21 +0800 Subject: [PATCH 20/46] fix --- neo/SmartContract/ContractParametersContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 49dec1b4dc..c430859d7a 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -102,7 +102,7 @@ public bool Add(Contract contract, int index, object parameter) public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { - if (contract.Script.IsMultiSigContract()) + if (contract.Script.IsMultiSigContract(out _, out _)) { ContextItem item = CreateItem(contract); if (item == null) return false; From fb764e742e1abd09631bf997883267c96a00819e Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 19 Jun 2019 17:09:48 +0800 Subject: [PATCH 21/46] fix tests --- neo.UnitTests/TestUtils.cs | 2 +- neo.UnitTests/UT_Block.cs | 10 +++++----- neo.UnitTests/UT_Header.cs | 4 ++-- neo.UnitTests/UT_Transaction.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 52f41de2e7..2a67f0def3 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -61,7 +61,7 @@ public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal) { bb.PrevHash = val256; - merkRootVal = new UInt256(new byte[] { 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197 }); + merkRootVal = new UInt256(new byte[] { 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216 }); bb.MerkleRoot = merkRootVal; timestampVal = new DateTime(1968, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(); bb.Timestamp = timestampVal; diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index f4e73decc0..886f5099f0 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -94,7 +94,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; + byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) @@ -111,7 +111,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -214,11 +214,11 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0xe0b482b6e4c176c9af520dd7caa6d80a9aeaeb80d016e788c702b05f5ac5ba4b"); + jObj["hash"].AsString().Should().Be("0x1d8642796276c8ce3c5c03b8984a1b593d99b49a63d830bb06f800b8c953be77"); jObj["size"].AsNumber().Should().Be(161); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0xc59eab3a287f74405b80325bc26c66ac7ef18d94c434a18d60950d3f098280f2"); + jObj["merkleroot"].AsString().Should().Be("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); jObj["time"].AsNumber().Should().Be(4244941696); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); @@ -229,7 +229,7 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0x7647acf1ef8a841b87f2369398a0e9f0ccde0ed9e835d980657103da6da59580"); + txObj[0]["hash"].AsString().Should().Be("0x64ed4e0d79407c60bde534feb44fbbd19bd065282d27ecd3a1a7a647f66affa6"); txObj[0]["size"].AsNumber().Should().Be(51); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); diff --git a/neo.UnitTests/UT_Header.cs b/neo.UnitTests/UT_Header.cs index 5cf0f84764..82744a7f8b 100644 --- a/neo.UnitTests/UT_Header.cs +++ b/neo.UnitTests/UT_Header.cs @@ -35,7 +35,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; + byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -106,7 +106,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; + byte[] requiredData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 4e442afcd8..fdfdcf9495 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -82,7 +82,7 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x38274692538dfecaae36f8fd518d92bae25607d491c40a8f927cc06bd97ab2c8"); + jObj["hash"].AsString().Should().Be("0xee00d595ccd48a650f62adaccbb9c979e2dc7ef66fb5b1413f0f74d563a2d9c6"); jObj["size"].AsNumber().Should().Be(82); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); From c0022c9dda91ef9beaa0cc73e5370139320fd60a Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 19 Jun 2019 17:29:02 +0800 Subject: [PATCH 22/46] Update MemoryPool.cs --- neo/Ledger/MemoryPool.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 4fb9a3174c..f4fee9bdf2 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -61,7 +61,6 @@ public class MemoryPool : IReadOnlyCollection internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; private int _maxTxPerBlock; - private long _feePerByte; /// /// Total maximum capacity of transactions the pool can hold. @@ -103,7 +102,6 @@ public MemoryPool(NeoSystem system, int capacity) internal void LoadPolicy(Snapshot snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); - _feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); } /// @@ -461,8 +459,7 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, Snapsho if (_unverifiedSortedTransactions.Count > 0) { - // Always leave at least 1 tx for low priority tx - int verifyCount = _sortedTransactions.Count > _maxTxPerBlock || maxToVerify == 1 ? 1 : maxToVerify - 1; + int verifyCount = _sortedTransactions.Count > _maxTxPerBlock ? 1 : maxToVerify; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, verifyCount, MaxSecondsToReverifyTxPerIdle, snapshot); } From bcb765dd00ca3128d7a2d9b3b2d7f03e837c22e0 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 19 Jun 2019 17:36:25 +0800 Subject: [PATCH 23/46] fix tests --- neo.UnitTests/UT_MemoryPool.cs | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 3c577eeac1..f10d84b29d 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -109,36 +109,24 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(60); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(9); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(51); + _unit.SortedTxCount.ShouldBeEquivalentTo(10); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(50); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(18); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(42); + _unit.SortedTxCount.ShouldBeEquivalentTo(20); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(40); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(27); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(33); - - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(36); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(24); - - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(45); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(15); - - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(54); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(6); + _unit.SortedTxCount.ShouldBeEquivalentTo(30); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(60); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(40); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(20); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(60); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(50); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(10); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); _unit.SortedTxCount.ShouldBeEquivalentTo(60); @@ -196,7 +184,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var minTransaction = sortedUnverifiedArray.Last(); // reverify 1 high priority and 1 low priority transaction - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot()); var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray(); verifiedTxs.Length.ShouldBeEquivalentTo(1); verifiedTxs[0].ShouldBeEquivalentTo(maxTransaction); From 1021dd628cdfe03ba67a5980e74693138dcf7734 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 10:09:04 +0200 Subject: [PATCH 24/46] UT - Good calculation! --- neo.UnitTests/UT_Transaction.cs | 67 ++++++++++++++++++ neo/SmartContract/Native/Tokens/Nep5Token.cs | 2 +- neo/Wallets/Wallet.cs | 73 ++++++++++---------- 3 files changed, 104 insertions(+), 38 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index fdfdcf9495..3e44a54575 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -2,7 +2,15 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.IO.Json; +using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.SmartContract.Native.Tokens; +using Neo.VM; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using Neo.Wallets.SQLite; namespace Neo.UnitTests { @@ -67,6 +75,65 @@ public void Size_Get() uut.Size.Should().Be(82); } + [TestMethod] + public void FeeIsSignatureContract() + { + var store = TestBlockchain.GetStore(); + var wallet = new NEP6Wallet("unit-test.json"); + + using (var unlock = wallet.Unlock("123")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateAccountKey(acc.ScriptHash); + var snapshot = store.GetSnapshot(); + snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray() + }); + + // Make transaction + + var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = acc.ScriptHash, + Value = new BigDecimal(1,8) + } + }, acc.ScriptHash); + + // Sign + + var data = new ContractParametersContext(tx); + Assert.IsTrue(wallet.Sign(data)); + tx.Witnesses = data.GetWitnesses(); + + // Check + + long executionGas = 0; + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(tx.Witnesses[0].VerificationScript); + engine.LoadScript(tx.Witnesses[0].InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + executionGas = engine.GasConsumed; + } + + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + Assert.AreEqual(tx.NetworkFee, executionGas + sizeGas); + } + } + [TestMethod] public void ToJson() { diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index 8234bbcac4..879faf5ab0 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -59,7 +59,7 @@ protected Nep5Token() Manifest.Abi.Events = events.ToArray(); } - protected StorageKey CreateAccountKey(UInt160 account) + internal protected StorageKey CreateAccountKey(UInt160 account) { return CreateStorageKey(Prefix_Account, account); } diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 05843ef8be..53734f138a 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -202,7 +202,7 @@ public virtual WalletAccount Import(string nep2, string passphrase) return account; } - public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null) + public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, UInt160 from = null) { UInt160[] accounts; if (from is null) @@ -214,51 +214,52 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null if (!Contains(from)) return null; accounts = new[] { from }; } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + + HashSet cosigners = new HashSet(); + byte[] script; + List<(UInt160 Account, BigInteger Value)> balances_gas = null; + using (ScriptBuilder sb = new ScriptBuilder()) { - HashSet cosigners = new HashSet(); - byte[] script; - List<(UInt160 Account, BigInteger Value)> balances_gas = null; - using (ScriptBuilder sb = new ScriptBuilder()) + foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) { - foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) + var balances = new List<(UInt160 Account, BigInteger Value)>(); + foreach (UInt160 account in accounts) + using (ScriptBuilder sb2 = new ScriptBuilder()) + { + sb2.EmitAppCall(assetId, "balanceOf", account); + ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true); + if (engine.State.HasFlag(VMState.FAULT)) return null; + BigInteger value = engine.ResultStack.Pop().GetBigInteger(); + if (value.Sign > 0) balances.Add((account, value)); + } + BigInteger sum_balance = balances.Select(p => p.Value).Sum(); + if (sum_balance < sum) return null; + foreach (TransferOutput output in group) { - var balances = new List<(UInt160 Account, BigInteger Value)>(); - foreach (UInt160 account in accounts) - using (ScriptBuilder sb2 = new ScriptBuilder()) - { - sb2.EmitAppCall(assetId, "balanceOf", account); - ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot); - if (engine.State.HasFlag(VMState.FAULT)) return null; - BigInteger value = engine.ResultStack.Pop().GetBigInteger(); - if (value.Sign > 0) balances.Add((account, value)); - } - BigInteger sum_balance = balances.Select(p => p.Value).Sum(); - if (sum_balance < sum) return null; - foreach (TransferOutput output in group) + balances = balances.OrderBy(p => p.Value).ToList(); + var balances_used = FindPayingAccounts(balances, output.Value.Value); + cosigners.UnionWith(balances_used.Select(p => p.Account)); + foreach (var (account, value) in balances_used) { - balances = balances.OrderBy(p => p.Value).ToList(); - var balances_used = FindPayingAccounts(balances, output.Value.Value); - cosigners.UnionWith(balances_used.Select(p => p.Account)); - foreach (var (account, value) in balances_used) + if (account != output.ScriptHash) { sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); sb.Emit(OpCode.THROWIFNOT); } } - if (assetId.Equals(NativeContract.GAS.Hash)) - balances_gas = balances; } - script = sb.ToArray(); + if (assetId.Equals(NativeContract.GAS.Hash)) + balances_gas = balances; } - if (balances_gas is null) - balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); - return MakeTransaction(snapshot, attributes, script, balances_gas); + script = sb.ToArray(); } + if (balances_gas is null) + balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } - public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) + public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) { UInt160[] accounts; if (sender is null) @@ -270,11 +271,9 @@ public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] scr if (!Contains(sender)) return null; accounts = new[] { sender }; } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - { - var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - return MakeTransaction(snapshot, attributes, script, balances_gas); - } + + var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) From ae382435e7a81958e0fe2153f128ccb77ca8929c Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 10:17:39 +0200 Subject: [PATCH 25/46] Clean --- neo.UnitTests/UT_Transaction.cs | 10 +++++----- neo/SmartContract/Native/NativeContract.cs | 2 +- neo/SmartContract/Native/Tokens/Nep5Token.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 3e44a54575..558b445cf7 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -80,6 +80,7 @@ public void FeeIsSignatureContract() { var store = TestBlockchain.GetStore(); var wallet = new NEP6Wallet("unit-test.json"); + var snapshot = store.GetSnapshot(); using (var unlock = wallet.Unlock("123")) { @@ -87,8 +88,7 @@ public void FeeIsSignatureContract() // Fake balance - var key = NativeContract.GAS.CreateAccountKey(acc.ScriptHash); - var snapshot = store.GetSnapshot(); + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); snapshot.Storages.GetAndChange(key, () => new StorageItem { Value = new Nep5AccountState() @@ -118,7 +118,7 @@ public void FeeIsSignatureContract() // Check - long executionGas = 0; + long verificationGas = 0; using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) { engine.LoadScript(tx.Witnesses[0].VerificationScript); @@ -126,11 +126,11 @@ public void FeeIsSignatureContract() Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); - executionGas = engine.GasConsumed; + verificationGas = engine.GasConsumed; } var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(tx.NetworkFee, executionGas + sizeGas); + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } } diff --git a/neo/SmartContract/Native/NativeContract.cs b/neo/SmartContract/Native/NativeContract.cs index 22c54ac127..41a700ee9c 100644 --- a/neo/SmartContract/Native/NativeContract.cs +++ b/neo/SmartContract/Native/NativeContract.cs @@ -79,7 +79,7 @@ protected StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } - protected StorageKey CreateStorageKey(byte prefix, ISerializable key) + internal protected StorageKey CreateStorageKey(byte prefix, ISerializable key) { return CreateStorageKey(prefix, key.ToArray()); } diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index 879faf5ab0..5b566e8d60 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -1,4 +1,4 @@ -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 using Neo.Ledger; using Neo.Persistence; @@ -59,7 +59,7 @@ protected Nep5Token() Manifest.Abi.Events = events.ToArray(); } - internal protected StorageKey CreateAccountKey(UInt160 account) + protected StorageKey CreateAccountKey(UInt160 account) { return CreateStorageKey(Prefix_Account, account); } From 1b0a2dfe834e0e11b09926ba4feab901456bf8c1 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 20 Jun 2019 20:22:01 +0800 Subject: [PATCH 26/46] Revert Nep5Token.cs --- neo/SmartContract/Native/Tokens/Nep5Token.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index 5b566e8d60..8234bbcac4 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -1,4 +1,4 @@ -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 using Neo.Ledger; using Neo.Persistence; From 071cee5c855f62ebefd6499961e13a4a684d18f5 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 15:35:27 +0200 Subject: [PATCH 27/46] Revert conditional --- neo/Wallets/Wallet.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 53734f138a..f318bf2ba4 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -241,11 +241,8 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, cosigners.UnionWith(balances_used.Select(p => p.Account)); foreach (var (account, value) in balances_used) { - if (account != output.ScriptHash) - { - sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); - sb.Emit(OpCode.THROWIFNOT); - } + sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); + sb.Emit(OpCode.THROWIFNOT); } } if (assetId.Equals(NativeContract.GAS.Hash)) From cd61b983e3a61aef43621e3e38a507381051ebaa Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 16:04:40 +0200 Subject: [PATCH 28/46] Improve readability --- neo/Wallets/Wallet.cs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index f318bf2ba4..abfb2de385 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -276,6 +276,10 @@ public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] att private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) { Random rand = new Random(); + long priceBytes64 = ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64]; + long priceBytes33 = ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33]; + long priceCheckSig = InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + foreach (var (account, value) in balances_gas) { Transaction tx = new Transaction @@ -310,19 +314,24 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at if (script.IsSignatureContract()) { size += 66 + script.GetVarSize(); - tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + tx.NetworkFee += priceBytes64 + priceBytes33 + priceCheckSig; } else if (script.IsMultiSigContract(out int m, out int n)) { + long feeM, feeN; + using (ScriptBuilder sb = new ScriptBuilder()) + { + feeM = ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + feeN = ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[1]]; + } + int size_inv = 65 * m; size += IO.Helper.GetVarSize(size_inv) + size_inv + script.GetVarSize(); - tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; - using (ScriptBuilder sb = new ScriptBuilder()) - tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; - tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; - using (ScriptBuilder sb = new ScriptBuilder()) - tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; + tx.NetworkFee += priceBytes64 * m; + tx.NetworkFee += feeM; + tx.NetworkFee += priceBytes33 * n; + tx.NetworkFee += feeN; + tx.NetworkFee += priceCheckSig * n; } else { From 5840174b73353dc62023f56c24641557423d8a77 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 16:04:40 +0200 Subject: [PATCH 29/46] Revert "Improve readability" This reverts commit cd61b983e3a61aef43621e3e38a507381051ebaa. --- neo/Wallets/Wallet.cs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index abfb2de385..f318bf2ba4 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -276,10 +276,6 @@ public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] att private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) { Random rand = new Random(); - long priceBytes64 = ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64]; - long priceBytes33 = ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33]; - long priceCheckSig = InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); - foreach (var (account, value) in balances_gas) { Transaction tx = new Transaction @@ -314,24 +310,19 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at if (script.IsSignatureContract()) { size += 66 + script.GetVarSize(); - tx.NetworkFee += priceBytes64 + priceBytes33 + priceCheckSig; + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); } else if (script.IsMultiSigContract(out int m, out int n)) { - long feeM, feeN; - using (ScriptBuilder sb = new ScriptBuilder()) - { - feeM = ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; - feeN = ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[1]]; - } - int size_inv = 65 * m; size += IO.Helper.GetVarSize(size_inv) + size_inv + script.GetVarSize(); - tx.NetworkFee += priceBytes64 * m; - tx.NetworkFee += feeM; - tx.NetworkFee += priceBytes33 * n; - tx.NetworkFee += feeN; - tx.NetworkFee += priceCheckSig * n; + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; + using (ScriptBuilder sb = new ScriptBuilder()) + tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; + using (ScriptBuilder sb = new ScriptBuilder()) + tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; + tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; } else { From 7fc2809d0717fdd06e579310f05f14dee8b2344f Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 18:26:28 +0200 Subject: [PATCH 30/46] Possible fix for unit test --- neo.UnitTests/UT_Transaction.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 558b445cf7..eb37bfc18e 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -10,7 +10,6 @@ using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; -using Neo.Wallets.SQLite; namespace Neo.UnitTests { @@ -89,15 +88,18 @@ public void FeeIsSignatureContract() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - snapshot.Storages.GetAndChange(key, () => new StorageItem + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem { - Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray() + Value = new Nep5AccountState().ToByteArray() }); + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + // Make transaction var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] @@ -110,6 +112,8 @@ public void FeeIsSignatureContract() } }, acc.ScriptHash); + Assert.IsNotNull(tx); + // Sign var data = new ContractParametersContext(tx); From 72782c7467483a04d314bbee6eaa2704fc202935 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 18:38:36 +0200 Subject: [PATCH 31/46] Error verbosity --- neo/Wallets/Wallet.cs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index f318bf2ba4..526956b720 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -211,7 +211,10 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, } else { - if (!Contains(from)) return null; + if (!Contains(from)) + { + throw new ArgumentException($"The address {from.ToString()} was not found in the wallet"); + } accounts = new[] { from }; } @@ -228,12 +231,18 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, { sb2.EmitAppCall(assetId, "balanceOf", account); ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true); - if (engine.State.HasFlag(VMState.FAULT)) return null; + if (engine.State.HasFlag(VMState.FAULT)) + { + throw new ArgumentException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); + } BigInteger value = engine.ResultStack.Pop().GetBigInteger(); if (value.Sign > 0) balances.Add((account, value)); } BigInteger sum_balance = balances.Select(p => p.Value).Sum(); - if (sum_balance < sum) return null; + if (sum_balance < sum) + { + throw new ArgumentException($"It does not have enough balance, expected: {sum.ToString()} found: {sum_balance.ToString()}"); + } foreach (TransferOutput output in group) { balances = balances.OrderBy(p => p.Value).ToList(); @@ -265,7 +274,10 @@ public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] att } else { - if (!Contains(sender)) return null; + if (!Contains(sender)) + { + throw new ArgumentException($"The address {sender.ToString()} was not found in the wallet"); + } accounts = new[] { sender }; } @@ -289,7 +301,10 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at }; using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, tx, testMode: true)) { - if (engine.State.HasFlag(VMState.FAULT)) return null; + if (engine.State.HasFlag(VMState.FAULT)) + { + throw new ArgumentException($"Failed execution for '{script.ToHexString()}'"); + } tx.Gas = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); if (tx.Gas > 0) { From 3121228d608d6d197cc079e59d908e70ad4c0451 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 18:43:31 +0200 Subject: [PATCH 32/46] Add using for ApplicationEngine --- neo/Wallets/Wallet.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 526956b720..696123d1e2 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -154,7 +154,7 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N byte[] encryptedkey = new byte[32]; Buffer.BlockCopy(data, 7, encryptedkey, 0, 32); byte[] prikey = XOR(encryptedkey.AES256Decrypt(derivedhalf2), derivedhalf1); - Cryptography.ECC.ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; + ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; UInt160 script_hash = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); string address = script_hash.ToAddress(); if (!Encoding.ASCII.GetBytes(address).Sha256().Sha256().Take(4).SequenceEqual(addresshash)) @@ -230,13 +230,15 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, using (ScriptBuilder sb2 = new ScriptBuilder()) { sb2.EmitAppCall(assetId, "balanceOf", account); - ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true); - if (engine.State.HasFlag(VMState.FAULT)) + using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) { - throw new ArgumentException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); + if (engine.State.HasFlag(VMState.FAULT)) + { + throw new ArgumentException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); + } + BigInteger value = engine.ResultStack.Pop().GetBigInteger(); + if (value.Sign > 0) balances.Add((account, value)); } - BigInteger value = engine.ResultStack.Pop().GetBigInteger(); - if (value.Sign > 0) balances.Add((account, value)); } BigInteger sum_balance = balances.Select(p => p.Value).Sum(); if (sum_balance < sum) From 85527bf775cb70468fe18520a3745a8136f2dbcd Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 18:56:34 +0200 Subject: [PATCH 33/46] Fix unit test --- neo.UnitTests/UT_Syscalls.cs | 4 +++- neo.UnitTests/UT_Transaction.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/neo.UnitTests/UT_Syscalls.cs b/neo.UnitTests/UT_Syscalls.cs index 82c857b6d9..c6642c4e88 100644 --- a/neo.UnitTests/UT_Syscalls.cs +++ b/neo.UnitTests/UT_Syscalls.cs @@ -27,7 +27,9 @@ public void System_Runtime_GetInvocationCounter() var contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; var contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; - contracts.DeleteWhere((a, b) => true); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); contracts.Add(contractA.ScriptHash, contractA); contracts.Add(contractB.ScriptHash, contractB); contracts.Add(contractC.ScriptHash, contractC); diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index eb37bfc18e..e4274b3aee 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -4,6 +4,7 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Tokens; @@ -17,11 +18,13 @@ namespace Neo.UnitTests public class UT_Transaction { Transaction uut; + Store store; [TestInitialize] public void TestSetup() { uut = new Transaction(); + store = TestBlockchain.GetStore(); } [TestMethod] @@ -77,7 +80,6 @@ public void Size_Get() [TestMethod] public void FeeIsSignatureContract() { - var store = TestBlockchain.GetStore(); var wallet = new NEP6Wallet("unit-test.json"); var snapshot = store.GetSnapshot(); From b2a638df508e29904d0d7578f9e9d487917075de Mon Sep 17 00:00:00 2001 From: Igor Machado Date: Thu, 20 Jun 2019 17:21:35 -0300 Subject: [PATCH 34/46] more readable merkleroot test --- neo.UnitTests/TestUtils.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 2a67f0def3..d806ca9b9c 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -61,7 +61,8 @@ public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal) { bb.PrevHash = val256; - merkRootVal = new UInt256(new byte[] { 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216 }); + merkRootVal = UInt256.Parse("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); + bb.MerkleRoot = merkRootVal; timestampVal = new DateTime(1968, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(); bb.Timestamp = timestampVal; From 9ac9412ffef0c42123160a165bf7508986f2f9aa Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 21 Jun 2019 10:27:59 +0200 Subject: [PATCH 35/46] Sample for multisig contract --- neo.UnitTests/UT_Transaction.cs | 85 +++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index e4274b3aee..f50dc49476 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.IO.Json; using Neo.Ledger; @@ -77,6 +78,90 @@ public void Size_Get() uut.Size.Should().Be(82); } + [TestMethod] + public void FeeIsMultiSigContract() + { + var store = TestBlockchain.GetStore(); + var walletA = new NEP6Wallet("unit-test-A.json"); + var walletB = new NEP6Wallet("unit-test-B.json"); + var walletC = new NEP6Wallet("unit-test-C.json"); + var snapshot = store.GetSnapshot(); + + using (var unlockA = walletA.Unlock("123")) + using (var unlockB = walletB.Unlock("123")) + using (var unlockC = walletC.Unlock("123")) + { + var a = walletA.CreateAccount(); + var b = walletB.CreateAccount(); + + var multiSignContract = Contract.CreateMultiSigContract(1, + new ECPoint[] + { + a.GetKey().PublicKey, + b.GetKey().PublicKey + } + ); + + var acc = walletC.CreateAccount(multiSignContract); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + + var tx = walletC.MakeTransaction(snapshot, new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = acc.ScriptHash, + Value = new BigDecimal(1,8) + } + }, acc.ScriptHash); + + Assert.IsNotNull(tx); + + // Sign + + // TODO: multi sign transaction + + var data = new ContractParametersContext(tx); + Assert.IsTrue(walletA.Sign(data)); + Assert.IsTrue(walletB.Sign(data)); + tx.Witnesses = data.GetWitnesses(); + + // Check + + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + [TestMethod] public void FeeIsSignatureContract() { From 1ce9d1679dcdab94290992562c15e9df0e93e415 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 21 Jun 2019 10:49:33 +0200 Subject: [PATCH 36/46] Sign ut --- neo.UnitTests/UT_Transaction.cs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index f50dc49476..206aa538fd 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -12,6 +12,7 @@ using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; +using System.Linq; namespace Neo.UnitTests { @@ -84,12 +85,10 @@ public void FeeIsMultiSigContract() var store = TestBlockchain.GetStore(); var walletA = new NEP6Wallet("unit-test-A.json"); var walletB = new NEP6Wallet("unit-test-B.json"); - var walletC = new NEP6Wallet("unit-test-C.json"); var snapshot = store.GetSnapshot(); using (var unlockA = walletA.Unlock("123")) using (var unlockB = walletB.Unlock("123")) - using (var unlockC = walletC.Unlock("123")) { var a = walletA.CreateAccount(); var b = walletB.CreateAccount(); @@ -102,7 +101,8 @@ public void FeeIsMultiSigContract() } ); - var acc = walletC.CreateAccount(multiSignContract); + var acc = walletA.CreateAccount(multiSignContract, a.GetKey()); + acc = walletB.CreateAccount(multiSignContract, b.GetKey()); // Fake balance @@ -120,7 +120,7 @@ public void FeeIsMultiSigContract() // Make transaction - var tx = walletC.MakeTransaction(snapshot, new TransferOutput[] + var tx = walletA.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -134,12 +134,17 @@ public void FeeIsMultiSigContract() // Sign - // TODO: multi sign transaction + var dataA = new ContractParametersContext(tx); + Assert.IsTrue(walletA.Sign(dataA)); - var data = new ContractParametersContext(tx); - Assert.IsTrue(walletA.Sign(data)); - Assert.IsTrue(walletB.Sign(data)); - tx.Witnesses = data.GetWitnesses(); + var dataB = new ContractParametersContext(tx); + Assert.IsTrue(walletB.Sign(dataB)); + + tx.Witnesses = dataA.GetWitnesses().Concat(dataB.GetWitnesses()).ToArray(); + + // Fast check + + // Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); // Check @@ -207,6 +212,10 @@ public void FeeIsSignatureContract() Assert.IsTrue(wallet.Sign(data)); tx.Witnesses = data.GetWitnesses(); + // Fast check + + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + // Check long verificationGas = 0; From 91790800bb36d5a0381ff58c4dc69ea2a6e8d186 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 21 Jun 2019 10:53:44 +0200 Subject: [PATCH 37/46] Signed! --- neo.UnitTests/UT_Transaction.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 206aa538fd..7640199819 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -93,7 +93,7 @@ public void FeeIsMultiSigContract() var a = walletA.CreateAccount(); var b = walletB.CreateAccount(); - var multiSignContract = Contract.CreateMultiSigContract(1, + var multiSignContract = Contract.CreateMultiSigContract(2, new ECPoint[] { a.GetKey().PublicKey, @@ -134,17 +134,16 @@ public void FeeIsMultiSigContract() // Sign - var dataA = new ContractParametersContext(tx); - Assert.IsTrue(walletA.Sign(dataA)); - - var dataB = new ContractParametersContext(tx); - Assert.IsTrue(walletB.Sign(dataB)); + var data = new ContractParametersContext(tx); + Assert.IsTrue(walletA.Sign(data)); + Assert.IsTrue(walletB.Sign(data)); + Assert.IsTrue(data.Completed); - tx.Witnesses = dataA.GetWitnesses().Concat(dataB.GetWitnesses()).ToArray(); + tx.Witnesses = data.GetWitnesses(); // Fast check - // Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); // Check From 1f36f844226e6961474b1e27d682fd6f4dc3867c Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 21 Jun 2019 10:55:42 +0200 Subject: [PATCH 38/46] Same format for single signature unit test --- neo.UnitTests/UT_Transaction.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 7640199819..92959bf167 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -218,14 +218,17 @@ public void FeeIsSignatureContract() // Check long verificationGas = 0; - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + foreach (var witness in tx.Witnesses) { - engine.LoadScript(tx.Witnesses[0].VerificationScript); - engine.LoadScript(tx.Witnesses[0].InvocationScript); - Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); - verificationGas = engine.GasConsumed; + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } } var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); From fc3e72988f51248c01280b08fe1d914945a82192 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 21 Jun 2019 11:13:21 +0200 Subject: [PATCH 39/46] Prevent create unwanted objects --- neo/SmartContract/ContractParametersContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index c430859d7a..335cad5b0c 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -183,7 +183,10 @@ private ContextItem CreateItem(Contract contract) public static ContractParametersContext FromJson(JObject json) { - if (!(typeof(ContractParametersContext).GetTypeInfo().Assembly.CreateInstance(json["type"].AsString()) is IVerifiable verifiable)) throw new FormatException(); + var type = typeof(ContractParametersContext).GetTypeInfo().Assembly.GetType(json["type"].AsString()); + if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); + + var verifiable = (IVerifiable)Activator.CreateInstance(type); using (MemoryStream ms = new MemoryStream(json["hex"].AsString().HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { From e484fbe16da44f1e9ecdcf8ba799e363082ad01b Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 21 Jun 2019 18:03:13 +0800 Subject: [PATCH 40/46] format --- neo/Wallets/Wallet.cs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 696123d1e2..cf111a0ae0 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -212,12 +212,9 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, else { if (!Contains(from)) - { throw new ArgumentException($"The address {from.ToString()} was not found in the wallet"); - } accounts = new[] { from }; } - HashSet cosigners = new HashSet(); byte[] script; List<(UInt160 Account, BigInteger Value)> balances_gas = null; @@ -233,18 +230,14 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) - { - throw new ArgumentException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); - } + throw new InvalidOperationException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); BigInteger value = engine.ResultStack.Pop().GetBigInteger(); if (value.Sign > 0) balances.Add((account, value)); } } BigInteger sum_balance = balances.Select(p => p.Value).Sum(); if (sum_balance < sum) - { - throw new ArgumentException($"It does not have enough balance, expected: {sum.ToString()} found: {sum_balance.ToString()}"); - } + throw new InvalidOperationException($"It does not have enough balance, expected: {sum.ToString()} found: {sum_balance.ToString()}"); foreach (TransferOutput output in group) { balances = balances.OrderBy(p => p.Value).ToList(); @@ -277,12 +270,9 @@ public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] att else { if (!Contains(sender)) - { throw new ArgumentException($"The address {sender.ToString()} was not found in the wallet"); - } accounts = new[] { sender }; } - var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); return MakeTransaction(snapshot, attributes, script, balances_gas); } @@ -304,9 +294,7 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, tx, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) - { - throw new ArgumentException($"Failed execution for '{script.ToHexString()}'"); - } + throw new InvalidOperationException($"Failed execution for '{script.ToHexString()}'"); tx.Gas = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); if (tx.Gas > 0) { @@ -349,7 +337,7 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); if (value >= tx.Gas + tx.NetworkFee) return tx; } - return null; + throw new InvalidOperationException("Insufficient GAS"); } public bool Sign(ContractParametersContext context) From 5bbfd02ec9b7e7231a2465775cea01747b44dcaa Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 21 Jun 2019 18:15:48 +0800 Subject: [PATCH 41/46] Remove `snapshot` from `MakeTransaction()` --- neo.UnitTests/UT_Transaction.cs | 4 +- neo/Wallets/Wallet.cs | 82 ++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 92959bf167..e714799fe8 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -120,7 +120,7 @@ public void FeeIsMultiSigContract() // Make transaction - var tx = walletA.MakeTransaction(snapshot, new TransferOutput[] + var tx = walletA.MakeTransaction(new TransferOutput[] { new TransferOutput() { @@ -193,7 +193,7 @@ public void FeeIsSignatureContract() // Make transaction - var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] + var tx = wallet.MakeTransaction(new TransferOutput[] { new TransferOutput() { diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index cf111a0ae0..9ec3369090 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -202,7 +202,7 @@ public virtual WalletAccount Import(string nep2, string passphrase) return account; } - public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, UInt160 from = null) + public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null) { UInt160[] accounts; if (from is null) @@ -215,52 +215,55 @@ public Transaction MakeTransaction(Snapshot snapshot, TransferOutput[] outputs, throw new ArgumentException($"The address {from.ToString()} was not found in the wallet"); accounts = new[] { from }; } - HashSet cosigners = new HashSet(); - byte[] script; - List<(UInt160 Account, BigInteger Value)> balances_gas = null; - using (ScriptBuilder sb = new ScriptBuilder()) + using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) + HashSet cosigners = new HashSet(); + byte[] script; + List<(UInt160 Account, BigInteger Value)> balances_gas = null; + using (ScriptBuilder sb = new ScriptBuilder()) { - var balances = new List<(UInt160 Account, BigInteger Value)>(); - foreach (UInt160 account in accounts) - using (ScriptBuilder sb2 = new ScriptBuilder()) - { - sb2.EmitAppCall(assetId, "balanceOf", account); - using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) + foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) + { + var balances = new List<(UInt160 Account, BigInteger Value)>(); + foreach (UInt160 account in accounts) + using (ScriptBuilder sb2 = new ScriptBuilder()) { - if (engine.State.HasFlag(VMState.FAULT)) - throw new InvalidOperationException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); - BigInteger value = engine.ResultStack.Pop().GetBigInteger(); - if (value.Sign > 0) balances.Add((account, value)); + sb2.EmitAppCall(assetId, "balanceOf", account); + using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) + { + if (engine.State.HasFlag(VMState.FAULT)) + throw new InvalidOperationException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); + BigInteger value = engine.ResultStack.Pop().GetBigInteger(); + if (value.Sign > 0) balances.Add((account, value)); + } } - } - BigInteger sum_balance = balances.Select(p => p.Value).Sum(); - if (sum_balance < sum) - throw new InvalidOperationException($"It does not have enough balance, expected: {sum.ToString()} found: {sum_balance.ToString()}"); - foreach (TransferOutput output in group) - { - balances = balances.OrderBy(p => p.Value).ToList(); - var balances_used = FindPayingAccounts(balances, output.Value.Value); - cosigners.UnionWith(balances_used.Select(p => p.Account)); - foreach (var (account, value) in balances_used) + BigInteger sum_balance = balances.Select(p => p.Value).Sum(); + if (sum_balance < sum) + throw new InvalidOperationException($"It does not have enough balance, expected: {sum.ToString()} found: {sum_balance.ToString()}"); + foreach (TransferOutput output in group) { - sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); - sb.Emit(OpCode.THROWIFNOT); + balances = balances.OrderBy(p => p.Value).ToList(); + var balances_used = FindPayingAccounts(balances, output.Value.Value); + cosigners.UnionWith(balances_used.Select(p => p.Account)); + foreach (var (account, value) in balances_used) + { + sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); + sb.Emit(OpCode.THROWIFNOT); + } } + if (assetId.Equals(NativeContract.GAS.Hash)) + balances_gas = balances; } - if (assetId.Equals(NativeContract.GAS.Hash)) - balances_gas = balances; + script = sb.ToArray(); } - script = sb.ToArray(); + if (balances_gas is null) + balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } - if (balances_gas is null) - balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); - return MakeTransaction(snapshot, attributes, script, balances_gas); } - public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) + public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) { UInt160[] accounts; if (sender is null) @@ -273,8 +276,11 @@ public Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] att throw new ArgumentException($"The address {sender.ToString()} was not found in the wallet"); accounts = new[] { sender }; } - var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - return MakeTransaction(snapshot, attributes, script, balances_gas); + using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + { + var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + return MakeTransaction(snapshot, attributes, script, balances_gas); + } } private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) From 63f24fcdf6ea9834d677a646deddfd7de7c60541 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 21 Jun 2019 18:21:43 +0800 Subject: [PATCH 42/46] Rename --- neo.UnitTests/UT_Transaction.cs | 10 +++++----- neo/Ledger/Blockchain.cs | 4 ++-- neo/Network/P2P/Payloads/Transaction.cs | 20 ++++++++++---------- neo/SmartContract/Native/Tokens/GasToken.cs | 6 +++--- neo/Wallets/Wallet.cs | 12 ++++++------ 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index e714799fe8..2af04c2c7d 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -50,15 +50,15 @@ public void Script_Set() [TestMethod] public void Gas_Get() { - uut.Gas.Should().Be(0); + uut.SystemFee.Should().Be(0); } [TestMethod] public void Gas_Set() { long val = 4200000000; - uut.Gas = val; - uut.Gas.Should().Be(val); + uut.SystemFee = val; + uut.SystemFee.Should().Be(val); } [TestMethod] @@ -241,7 +241,7 @@ public void ToJson() { uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; - uut.Gas = 4200000000; + uut.SystemFee = 4200000000; uut.Attributes = new TransactionAttribute[0]; uut.Witnesses = new Witness[]{ new Witness { @@ -257,7 +257,7 @@ public void ToJson() ((JArray)jObj["attributes"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); jObj["script"].AsString().Should().Be("4220202020202020202020202020202020202020202020202020202020202020"); - jObj["gas"].AsNumber().Should().Be(42); + jObj["sys_fee"].AsNumber().Should().Be(42); } } } diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 9aad109789..059560573b 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -152,7 +152,7 @@ private static Transaction DeployNativeContracts() Version = 0, Script = script, Sender = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(), - Gas = 0, + SystemFee = 0, Attributes = new TransactionAttribute[0], Witnesses = new[] { @@ -436,7 +436,7 @@ private void Persist(Block block) BlockIndex = block.Index, Transaction = tx }); - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.Gas)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.SystemFee)) { engine.LoadScript(tx.Script); if (!engine.Execute().HasFlag(VMState.FAULT)) diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 87b0c8fc1f..152ac12b59 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -28,7 +28,7 @@ public class Transaction : IEquatable, IInventory /// /// Distributed to NEO holders. /// - public long Gas; + public long SystemFee; /// /// Distributed to consensus nodes. /// @@ -84,12 +84,12 @@ public void DeserializeUnsigned(BinaryReader reader) if (Version > 0) throw new FormatException(); Nonce = reader.ReadUInt32(); Sender = reader.ReadSerializable(); - Gas = reader.ReadInt64(); - if (Gas < 0) throw new FormatException(); - if (Gas % NativeContract.GAS.Factor != 0) throw new FormatException(); + SystemFee = reader.ReadInt64(); + if (SystemFee < 0) throw new FormatException(); + if (SystemFee % NativeContract.GAS.Factor != 0) throw new FormatException(); NetworkFee = reader.ReadInt64(); if (NetworkFee < 0) throw new FormatException(); - if (Gas + NetworkFee < Gas) throw new FormatException(); + if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); var cosigners = Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data)).ToArray(); @@ -133,7 +133,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(Version); writer.Write(Nonce); writer.Write(Sender); - writer.Write(Gas); + writer.Write(SystemFee); writer.Write(NetworkFee); writer.Write(ValidUntilBlock); writer.Write(Attributes); @@ -148,8 +148,8 @@ public JObject ToJson() json["version"] = Version; json["nonce"] = Nonce; json["sender"] = Sender.ToAddress(); - json["gas"] = new BigDecimal(Gas, (byte)NativeContract.GAS.Decimals).ToString(); - json["net_fee"] = new BigDecimal(NetworkFee, (byte)NativeContract.GAS.Decimals).ToString(); + json["sys_fee"] = new BigDecimal(SystemFee, NativeContract.GAS.Decimals).ToString(); + json["net_fee"] = new BigDecimal(NetworkFee, NativeContract.GAS.Decimals).ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); json["script"] = Script.ToHexString(); @@ -173,9 +173,9 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - BigInteger fee = Gas + NetworkFee; + BigInteger fee = SystemFee + NetworkFee; if (balance < fee) return false; - fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.Gas + p.NetworkFee)).Sum(); + fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); if (balance < fee) return false; return this.VerifyWitnesses(snapshot, net_fee); } diff --git a/neo/SmartContract/Native/Tokens/GasToken.cs b/neo/SmartContract/Native/Tokens/GasToken.cs index 0bcc303fe7..ca74a41855 100644 --- a/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/neo/SmartContract/Native/Tokens/GasToken.cs @@ -29,11 +29,11 @@ protected override bool OnPersist(ApplicationEngine engine) { if (!base.OnPersist(engine)) return false; foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) - Burn(engine, tx.Sender, tx.Gas + tx.NetworkFee); + Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.Snapshot.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.NetworkFee)); - BigInteger sys_fee = GetSysFeeAmount(engine.Snapshot, engine.Snapshot.PersistingBlock.Index - 1) + engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.Gas); + BigInteger sys_fee = GetSysFeeAmount(engine.Snapshot, engine.Snapshot.PersistingBlock.Index - 1) + engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.SystemFee); StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(engine.Snapshot.PersistingBlock.Index)); engine.Snapshot.Storages.Add(key, new StorageItem { @@ -52,7 +52,7 @@ private StackItem GetSysFeeAmount(ApplicationEngine engine, VMArray args) public BigInteger GetSysFeeAmount(Snapshot snapshot, uint index) { - if (index == 0) return Blockchain.GenesisBlock.Transactions.Sum(p => p.Gas); + if (index == 0) return Blockchain.GenesisBlock.Transactions.Sum(p => p.SystemFee); StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(index)); StorageItem storage = snapshot.Storages.TryGet(key); if (storage is null) return BigInteger.Zero; diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 9ec3369090..6358a02995 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -301,15 +301,15 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at { if (engine.State.HasFlag(VMState.FAULT)) throw new InvalidOperationException($"Failed execution for '{script.ToHexString()}'"); - tx.Gas = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); - if (tx.Gas > 0) + tx.SystemFee = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); + if (tx.SystemFee > 0) { long d = (long)NativeContract.GAS.Factor; - long remainder = tx.Gas % d; + long remainder = tx.SystemFee % d; if (remainder > 0) - tx.Gas += d - remainder; + tx.SystemFee += d - remainder; else if (remainder < 0) - tx.Gas -= remainder; + tx.SystemFee -= remainder; } } UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); @@ -341,7 +341,7 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at } } tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); - if (value >= tx.Gas + tx.NetworkFee) return tx; + if (value >= tx.SystemFee + tx.NetworkFee) return tx; } throw new InvalidOperationException("Insufficient GAS"); } From ff1d71e855e4ecfa9e47c252e255ca51f36cc84f Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 21 Jun 2019 18:40:18 +0800 Subject: [PATCH 43/46] format --- neo.UnitTests/TestUtils.cs | 11 +++++--- neo.UnitTests/UT_MemoryPool.cs | 11 +++++--- neo.UnitTests/UT_PoolItem.cs | 11 +++++--- neo.UnitTests/UT_Transaction.cs | 26 +++++++++++-------- .../ContractParametersContext.cs | 2 +- 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index d806ca9b9c..9bf06acbf7 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -87,11 +87,14 @@ public static Transaction CreateRandomHashTransaction() Script = randomBytes, Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], - Witnesses = new Witness[]{ new Witness + Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] - } } + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + } }; } diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index f10d84b29d..90cd43d5d5 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -53,11 +53,14 @@ private Transaction CreateTransactionWithFee(long fee) mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; mock.Object.Attributes = new TransactionAttribute[0]; - mock.Object.Witnesses = new Witness[]{ new Witness + mock.Object.Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] - } }; + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + }; return mock.Object; } diff --git a/neo.UnitTests/UT_PoolItem.cs b/neo.UnitTests/UT_PoolItem.cs index 107e5e8a9a..cedf7f3fa9 100644 --- a/neo.UnitTests/UT_PoolItem.cs +++ b/neo.UnitTests/UT_PoolItem.cs @@ -122,11 +122,14 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS Sender = UInt160.Zero, NetworkFee = networkFee, Attributes = new TransactionAttribute[0], - Witnesses = new Witness[]{ new Witness + Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] - } } + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + } }; int diff = size - tx.Size; diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 2af04c2c7d..1c810fdfc5 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -12,7 +12,6 @@ using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; -using System.Linq; namespace Neo.UnitTests { @@ -67,11 +66,14 @@ public void Size_Get() uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; uut.Attributes = new TransactionAttribute[0]; - uut.Witnesses = new Witness[]{ new Witness + uut.Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] - } }; + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + }; uut.Version.Should().Be(0); uut.Script.Length.Should().Be(32); @@ -98,8 +100,7 @@ public void FeeIsMultiSigContract() { a.GetKey().PublicKey, b.GetKey().PublicKey - } - ); + }); var acc = walletA.CreateAccount(multiSignContract, a.GetKey()); acc = walletB.CreateAccount(multiSignContract, b.GetKey()); @@ -243,11 +244,14 @@ public void ToJson() uut.Sender = UInt160.Zero; uut.SystemFee = 4200000000; uut.Attributes = new TransactionAttribute[0]; - uut.Witnesses = new Witness[]{ new Witness + uut.Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] - } }; + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + }; JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 335cad5b0c..8fda2c8450 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -262,4 +262,4 @@ public override string ToString() return ToJson().ToString(); } } -} \ No newline at end of file +} From 70ccc770d2d34338b4dd032bf8f8212884223749 Mon Sep 17 00:00:00 2001 From: Igor Machado Date: Fri, 21 Jun 2019 10:51:19 -0300 Subject: [PATCH 44/46] using json based NEP6Wallet import --- neo.UnitTests/UT_Transaction.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 1c810fdfc5..18aafb31bb 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -81,12 +81,24 @@ public void Size_Get() uut.Size.Should().Be(82); } + private NEP6Wallet GenerateTestWallet() + { + JObject wallet = new JObject(); + wallet["name"] = "noname"; + wallet["version"] = new System.Version().ToString(); + wallet["scrypt"] = ScryptParameters.Default.ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = null; + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":null}"); + return new NEP6Wallet(wallet); + } + [TestMethod] public void FeeIsMultiSigContract() { var store = TestBlockchain.GetStore(); - var walletA = new NEP6Wallet("unit-test-A.json"); - var walletB = new NEP6Wallet("unit-test-B.json"); + var walletA = GenerateTestWallet(); + var walletB = GenerateTestWallet(); var snapshot = store.GetSnapshot(); using (var unlockA = walletA.Unlock("123")) @@ -170,7 +182,7 @@ public void FeeIsMultiSigContract() [TestMethod] public void FeeIsSignatureContract() { - var wallet = new NEP6Wallet("unit-test.json"); + var wallet = GenerateTestWallet(); var snapshot = store.GetSnapshot(); using (var unlock = wallet.Unlock("123")) From dd3915274238b0fb2b581acf00011bb3f472cf7f Mon Sep 17 00:00:00 2001 From: Igor Machado Date: Fri, 21 Jun 2019 11:19:19 -0300 Subject: [PATCH 45/46] at least using read serializable array --- neo/Network/P2P/Payloads/BlockBase.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 9f56ca0d97..e991200803 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -50,8 +50,9 @@ Witness[] IVerifiable.Witnesses public virtual void Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); - if (reader.ReadByte() != 1) throw new FormatException(); - Witness = reader.ReadSerializable(); + Witness[] witnesses = reader.ReadSerializableArray(); + if (witnesses.Length != 1) throw new FormatException(); + Witness = witnesses[0]; } void IVerifiable.DeserializeUnsigned(BinaryReader reader) @@ -75,7 +76,8 @@ UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) public virtual void Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write((byte)1); writer.Write(Witness); + Witness[] witnesses = new Witness[]{ Witness }; + writer.Write(witnesses); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) From ccad415ed2e2df32ff6de23cac541997a5ade37f Mon Sep 17 00:00:00 2001 From: Igor Machado Date: Fri, 21 Jun 2019 11:29:58 -0300 Subject: [PATCH 46/46] revert last commit --- neo/Network/P2P/Payloads/BlockBase.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index e991200803..9f56ca0d97 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -50,9 +50,8 @@ Witness[] IVerifiable.Witnesses public virtual void Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); - Witness[] witnesses = reader.ReadSerializableArray(); - if (witnesses.Length != 1) throw new FormatException(); - Witness = witnesses[0]; + if (reader.ReadByte() != 1) throw new FormatException(); + Witness = reader.ReadSerializable(); } void IVerifiable.DeserializeUnsigned(BinaryReader reader) @@ -76,8 +75,7 @@ UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) public virtual void Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - Witness[] witnesses = new Witness[]{ Witness }; - writer.Write(witnesses); + writer.Write((byte)1); writer.Write(Witness); } void IVerifiable.SerializeUnsigned(BinaryWriter writer)