From 7de60e11a2fa3d37789c90ecdf180da6fc051f18 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Mon, 24 Jun 2019 21:06:43 +0800 Subject: [PATCH 1/6] Limit SYSCALLs in Verification --- neo.UnitTests/TestUtils.cs | 2 +- neo/Network/P2P/Payloads/Witness.cs | 4 +- neo/SmartContract/Helper.cs | 13 +- neo/SmartContract/InteropDescriptor.cs | 40 ++++++ neo/SmartContract/InteropService.NEO.cs | 62 +++++---- neo/SmartContract/InteropService.cs | 122 ++++++++---------- .../Native/ContractMethodAttribute.cs | 1 - .../Native/ContractMethodMetadata.cs | 1 - neo/SmartContract/Native/NativeContract.cs | 7 +- neo/SmartContract/Native/PolicyContract.cs | 8 +- neo/SmartContract/Native/Tokens/NeoToken.cs | 4 +- neo/SmartContract/Native/Tokens/Nep5Token.cs | 2 +- neo/SmartContract/WitnessWrapper.cs | 27 ---- 13 files changed, 138 insertions(+), 155 deletions(-) create mode 100644 neo/SmartContract/InteropDescriptor.cs delete mode 100644 neo/SmartContract/WitnessWrapper.cs diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 9bf06acbf7..c8625a4e04 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -92,7 +92,7 @@ public static Transaction CreateRandomHashTransaction() new Witness { InvocationScript = new byte[0], - VerificationScript = new byte[0] + VerificationScript = new byte[1] } } }; diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index 237a954642..b02d521dcc 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.SmartContract; -using Neo.VM; +using System; using System.IO; namespace Neo.Network.P2P.Payloads @@ -30,6 +30,8 @@ void ISerializable.Deserialize(BinaryReader reader) { InvocationScript = reader.ReadVarBytes(65536); VerificationScript = reader.ReadVarBytes(65536); + if (VerificationScript.Length == 0) + throw new FormatException(); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index f40dad212e..3cf40a42d8 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -262,19 +262,10 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snaps if (hashes.Length != verifiable.Witnesses.Length) return false; for (int i = 0; i < hashes.Length; i++) { - 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; - } + 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].VerificationScript); 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; diff --git a/neo/SmartContract/InteropDescriptor.cs b/neo/SmartContract/InteropDescriptor.cs new file mode 100644 index 0000000000..c283e474de --- /dev/null +++ b/neo/SmartContract/InteropDescriptor.cs @@ -0,0 +1,40 @@ +using Neo.VM; +using System; + +namespace Neo.SmartContract +{ + internal class InteropDescriptor + { + public string Method { get; } + public uint Hash { get; } + public Func Handler { get; } + public long Price { get; } + public Func, long> PriceCalculator { get; } + public TriggerType AllowedTriggers { get; } + + public InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers) + : this(method, handler, allowedTriggers) + { + this.Price = price; + } + + public InteropDescriptor(string method, Func handler, Func, long> priceCalculator, TriggerType allowedTriggers) + : this(method, handler, allowedTriggers) + { + this.PriceCalculator = priceCalculator; + } + + private InteropDescriptor(string method, Func handler, TriggerType allowedTriggers) + { + this.Method = method; + this.Hash = method.ToInteropMethodHash(); + this.Handler = handler; + this.AllowedTriggers = allowedTriggers; + } + + public long GetPrice(RandomAccessStack stack) + { + return PriceCalculator is null ? Price : PriceCalculator(stack); + } + } +} diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index a496497962..812a2d7155 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -18,37 +18,37 @@ namespace Neo.SmartContract { static partial class InteropService { - public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0); - public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000); - public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice); - public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400); - 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_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); - public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400); - public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400); - public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000); - public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400); - public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000); - public static readonly uint Neo_Enumerator_Value = Register("Neo.Enumerator.Value", Enumerator_Value, 0_00000400); - public static readonly uint Neo_Enumerator_Concat = Register("Neo.Enumerator.Concat", Enumerator_Concat, 0_00000400); - public static readonly uint Neo_Iterator_Create = Register("Neo.Iterator.Create", Iterator_Create, 0_00000400); - public static readonly uint Neo_Iterator_Key = Register("Neo.Iterator.Key", Iterator_Key, 0_00000400); - public static readonly uint Neo_Iterator_Keys = Register("Neo.Iterator.Keys", Iterator_Keys, 0_00000400); - public static readonly uint Neo_Iterator_Values = Register("Neo.Iterator.Values", Iterator_Values, 0_00000400); - public static readonly uint Neo_Iterator_Concat = Register("Neo.Iterator.Concat", Iterator_Concat, 0_00000400); - public static readonly uint Neo_Json_Serialize = Register("Neo.Json.Serialize", Json_Serialize, 0_00100000); - public static readonly uint Neo_Json_Deserialize = Register("Neo.Json.Deserialize", Json_Deserialize, 0_00500000); + public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); + public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000, TriggerType.All); + public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice, TriggerType.All); + public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400, TriggerType.All); + public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000, TriggerType.All); + public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400, TriggerType.All); + public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All); + public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); + public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); + public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); + public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); + public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); + public static readonly uint Neo_Enumerator_Value = Register("Neo.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All); + public static readonly uint Neo_Enumerator_Concat = Register("Neo.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Create = Register("Neo.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Key = Register("Neo.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Keys = Register("Neo.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Values = Register("Neo.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Concat = Register("Neo.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All); + public static readonly uint Neo_Json_Serialize = Register("Neo.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All); + public static readonly uint Neo_Json_Deserialize = Register("Neo.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All); static InteropService() { foreach (NativeContract contract in NativeContract.Contracts) - Register(contract.ServiceName, contract.Invoke, contract.GetPrice); + Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); } private static long GetCheckMultiSigPrice(RandomAccessStack stack) @@ -70,7 +70,6 @@ private static long GetDeploymentPrice(RandomAccessStack stack) private static bool Native_Deploy(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; if (engine.Snapshot.PersistingBlock.Index != 0) return false; foreach (NativeContract contract in NativeContract.Contracts) { @@ -215,7 +214,7 @@ private static bool Transaction_GetWitnesses(ApplicationEngine engine) 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()); + engine.CurrentContext.EvaluationStack.Push(tx.Witnesses.Select(p => StackItem.FromInterface(p)).ToArray()); return true; } return false; @@ -225,7 +224,7 @@ private static bool Witness_GetVerificationScript(ApplicationEngine engine) { if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { - WitnessWrapper witness = _interface.GetInterface(); + Witness witness = _interface.GetInterface(); if (witness == null) return false; engine.CurrentContext.EvaluationStack.Push(witness.VerificationScript); return true; @@ -244,7 +243,6 @@ private static bool Account_IsStandard(ApplicationEngine engine) private static bool Contract_Create(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); if (script.Length > 1024 * 1024) return false; @@ -269,8 +267,6 @@ private static bool Contract_Create(ApplicationEngine engine) private static bool Contract_Update(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); if (script.Length > 1024 * 1024) return false; var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 2574bd3753..3840dbe306 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -23,47 +23,45 @@ public static partial class InteropService public const int MaxStorageKeySize = 64; public const int MaxStorageValueSize = ushort.MaxValue; - private static readonly Dictionary> methods = new Dictionary>(); - private static readonly Dictionary prices = new Dictionary(); - private static readonly Dictionary, long>> priceCalculators = new Dictionary, long>>(); - - public static readonly uint System_ExecutionEngine_GetScriptContainer = Register("System.ExecutionEngine.GetScriptContainer", ExecutionEngine_GetScriptContainer, 0_00000250); - public static readonly uint System_ExecutionEngine_GetExecutingScriptHash = Register("System.ExecutionEngine.GetExecutingScriptHash", ExecutionEngine_GetExecutingScriptHash, 0_00000400); - public static readonly uint System_ExecutionEngine_GetCallingScriptHash = Register("System.ExecutionEngine.GetCallingScriptHash", ExecutionEngine_GetCallingScriptHash, 0_00000400); - public static readonly uint System_ExecutionEngine_GetEntryScriptHash = Register("System.ExecutionEngine.GetEntryScriptHash", ExecutionEngine_GetEntryScriptHash, 0_00000400); - public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250); - public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250); - public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000); - public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_00000250); - public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_00300000); - public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250); - public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000); - public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000); - public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400); - public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000); - public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400); - public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000); - public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000); - public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000); - public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000); - public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000); - public static readonly uint System_Header_GetIndex = Register("System.Header.GetIndex", Header_GetIndex, 0_00000400); - public static readonly uint System_Header_GetHash = Register("System.Header.GetHash", Header_GetHash, 0_00000400); - public static readonly uint System_Header_GetPrevHash = Register("System.Header.GetPrevHash", Header_GetPrevHash, 0_00000400); - public static readonly uint System_Header_GetTimestamp = Register("System.Header.GetTimestamp", Header_GetTimestamp, 0_00000400); - public static readonly uint System_Block_GetTransactionCount = Register("System.Block.GetTransactionCount", Block_GetTransactionCount, 0_00000400); - public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000); - public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400); - public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400); - public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000); - public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000); - public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400); - public static readonly uint System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400); - public static readonly uint System_Storage_Get = Register("System.Storage.Get", Storage_Get, 0_01000000); - public static readonly uint System_Storage_Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice); - public static readonly uint System_Storage_PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice); - public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000); - public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400); + private static readonly Dictionary methods = new Dictionary(); + + public static readonly uint System_ExecutionEngine_GetScriptContainer = Register("System.ExecutionEngine.GetScriptContainer", ExecutionEngine_GetScriptContainer, 0_00000250, TriggerType.All); + public static readonly uint System_ExecutionEngine_GetExecutingScriptHash = Register("System.ExecutionEngine.GetExecutingScriptHash", ExecutionEngine_GetExecutingScriptHash, 0_00000400, TriggerType.All); + public static readonly uint System_ExecutionEngine_GetCallingScriptHash = Register("System.ExecutionEngine.GetCallingScriptHash", ExecutionEngine_GetCallingScriptHash, 0_00000400, TriggerType.All); + public static readonly uint System_ExecutionEngine_GetEntryScriptHash = Register("System.ExecutionEngine.GetEntryScriptHash", ExecutionEngine_GetEntryScriptHash, 0_00000400, TriggerType.All); + public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); + public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); + public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); + public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_00000250, TriggerType.All); + public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_00300000, TriggerType.All); + public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); + public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000, TriggerType.All); + public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); + public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); + public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000, TriggerType.All); + public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); + public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000, TriggerType.Application); + public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); + public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); + public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); + public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); + public static readonly uint System_Header_GetIndex = Register("System.Header.GetIndex", Header_GetIndex, 0_00000400, TriggerType.Application); + public static readonly uint System_Header_GetHash = Register("System.Header.GetHash", Header_GetHash, 0_00000400, TriggerType.Application); + public static readonly uint System_Header_GetPrevHash = Register("System.Header.GetPrevHash", Header_GetPrevHash, 0_00000400, TriggerType.Application); + public static readonly uint System_Header_GetTimestamp = Register("System.Header.GetTimestamp", Header_GetTimestamp, 0_00000400, TriggerType.Application); + public static readonly uint System_Block_GetTransactionCount = Register("System.Block.GetTransactionCount", Block_GetTransactionCount, 0_00000400, TriggerType.Application); + public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000, TriggerType.Application); + public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400, TriggerType.Application); + public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400, TriggerType.All); + public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.Application); + public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); + public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); + public static readonly uint System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application); + public static readonly uint System_Storage_Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application); + public static readonly uint System_Storage_Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application); + public static readonly uint System_Storage_PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application); + public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); + public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400, TriggerType.Application); private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) { @@ -75,10 +73,7 @@ private static bool CheckStorageContext(ApplicationEngine engine, StorageContext public static long GetPrice(uint hash, RandomAccessStack stack) { - if (prices.TryGetValue(hash, out long price)) - return price; - else - return priceCalculators[hash](stack); + return methods[hash].GetPrice(stack); } private static long GetStoragePrice(RandomAccessStack stack) @@ -88,25 +83,25 @@ private static long GetStoragePrice(RandomAccessStack stack) internal static bool Invoke(ApplicationEngine engine, uint method) { - if (!methods.TryGetValue(method, out Func func)) + if (!methods.TryGetValue(method, out InteropDescriptor descriptor)) + return false; + if (!descriptor.AllowedTriggers.HasFlag(engine.Trigger)) return false; - return func(engine); + return descriptor.Handler(engine); } - private static uint Register(string method, Func handler, long price) + private static uint Register(string method, Func handler, long price, TriggerType allowedTriggers) { - uint hash = method.ToInteropMethodHash(); - methods.Add(hash, handler); - prices.Add(hash, price); - return hash; + InteropDescriptor descriptor = new InteropDescriptor(method, handler, price, allowedTriggers); + methods.Add(descriptor.Hash, descriptor); + return descriptor.Hash; } - private static uint Register(string method, Func handler, Func, long> priceCalculator) + private static uint Register(string method, Func handler, Func, long> priceCalculator, TriggerType allowedTriggers) { - uint hash = method.ToInteropMethodHash(); - methods.Add(hash, handler); - priceCalculators.Add(hash, priceCalculator); - return hash; + InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers); + methods.Add(descriptor.Hash, descriptor); + return descriptor.Hash; } private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine) @@ -185,15 +180,7 @@ private static bool Runtime_Log(ApplicationEngine engine) private static bool Runtime_GetTime(ApplicationEngine engine) { - if (engine.Snapshot.PersistingBlock == null) - { - Header header = engine.Snapshot.GetHeader(engine.Snapshot.CurrentBlockHash); - engine.CurrentContext.EvaluationStack.Push(header.Timestamp + Blockchain.SecondsPerBlock); - } - else - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); - } + engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); return true; } @@ -529,7 +516,6 @@ private static bool Contract_Call(ApplicationEngine engine) private static bool Contract_Destroy(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; UInt160 hash = engine.CurrentScriptHash; ContractState contract = engine.Snapshot.Contracts.TryGet(hash); if (contract == null) return true; @@ -542,7 +528,6 @@ private static bool Contract_Destroy(ApplicationEngine engine) private static bool PutEx(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) { - if (engine.Trigger != TriggerType.Application) return false; if (key.Length > MaxStorageKeySize) return false; if (value.Length > MaxStorageValueSize) return false; if (context.IsReadOnly) return false; @@ -593,7 +578,6 @@ private static bool Storage_PutEx(ApplicationEngine engine) private static bool Storage_Delete(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { StorageContext context = _interface.GetInterface(); diff --git a/neo/SmartContract/Native/ContractMethodAttribute.cs b/neo/SmartContract/Native/ContractMethodAttribute.cs index 145979a6cb..393737d92e 100644 --- a/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -7,7 +7,6 @@ internal class ContractMethodAttribute : Attribute { public string Name { get; set; } public long Price { get; } - public TriggerType AllowedTriggers { get; set; } = TriggerType.All; public ContractParameterType ReturnType { get; } public ContractParameterType[] ParameterTypes { get; set; } = new ContractParameterType[0]; public string[] ParameterNames { get; set; } = new string[0]; diff --git a/neo/SmartContract/Native/ContractMethodMetadata.cs b/neo/SmartContract/Native/ContractMethodMetadata.cs index 1c67b553e2..f63344eec9 100644 --- a/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -8,6 +8,5 @@ internal class ContractMethodMetadata { public Func Delegate; public long Price; - public TriggerType AllowedTriggers; } } diff --git a/neo/SmartContract/Native/NativeContract.cs b/neo/SmartContract/Native/NativeContract.cs index 41a700ee9c..08971dcf6b 100644 --- a/neo/SmartContract/Native/NativeContract.cs +++ b/neo/SmartContract/Native/NativeContract.cs @@ -57,8 +57,7 @@ protected NativeContract() methods.Add(name, new ContractMethodMetadata { Delegate = (Func)method.CreateDelegate(typeof(Func), this), - Price = attribute.Price, - AllowedTriggers = attribute.AllowedTriggers + Price = attribute.Price }); } this.Manifest.Abi.Methods = descriptors.ToArray(); @@ -92,7 +91,6 @@ internal bool Invoke(ApplicationEngine engine) VMArray args = (VMArray)engine.CurrentContext.EvaluationStack.Pop(); if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; - if (!method.AllowedTriggers.HasFlag(engine.Trigger)) return false; StackItem result = method.Delegate(engine, args); engine.CurrentContext.EvaluationStack.Push(result); return true; @@ -110,9 +108,10 @@ internal virtual bool Initialize(ApplicationEngine engine) return true; } - [ContractMethod(0, ContractParameterType.Boolean, AllowedTriggers = TriggerType.System)] + [ContractMethod(0, ContractParameterType.Boolean)] protected StackItem OnPersist(ApplicationEngine engine, VMArray args) { + if (engine.Trigger != TriggerType.System) return false; return OnPersist(engine); } diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index 03781a5cb7..2109e8b65d 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -84,7 +84,7 @@ public UInt160[] GetBlockedAccounts(Snapshot snapshot) return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; @@ -94,7 +94,7 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray a return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; @@ -104,7 +104,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem BlockAccount(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; @@ -118,7 +118,7 @@ private StackItem BlockAccount(ApplicationEngine engine, VMArray args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem UnblockAccount(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; diff --git a/neo/SmartContract/Native/Tokens/NeoToken.cs b/neo/SmartContract/Native/Tokens/NeoToken.cs index 5320171059..6de1ac1970 100644 --- a/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -132,7 +132,7 @@ public BigInteger UnclaimedGas(Snapshot snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); } - [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] private StackItem RegisterValidator(ApplicationEngine engine, VMArray args) { ECPoint pubkey = args[0].GetByteArray().AsSerializable(); @@ -150,7 +150,7 @@ private bool RegisterValidator(Snapshot snapshot, ECPoint pubkey) return true; } - [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] private StackItem Vote(ApplicationEngine engine, VMArray args) { UInt160 account = new UInt160(args[0].GetByteArray()); diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index 8234bbcac4..6a2e1c78b3 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -158,7 +158,7 @@ public virtual BigInteger BalanceOf(Snapshot snapshot, UInt160 account) return state.Balance; } - [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] protected StackItem Transfer(ApplicationEngine engine, VMArray args) { UInt160 from = new UInt160(args[0].GetByteArray()); diff --git a/neo/SmartContract/WitnessWrapper.cs b/neo/SmartContract/WitnessWrapper.cs deleted file mode 100644 index f67690cad5..0000000000 --- a/neo/SmartContract/WitnessWrapper.cs +++ /dev/null @@ -1,27 +0,0 @@ -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 f253fae1ce9cf44a2963e08950f092bbf284faa7 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 25 Jun 2019 10:12:29 +0800 Subject: [PATCH 2/6] Allow deployed contract as verification script --- neo.UnitTests/TestUtils.cs | 2 +- neo/Network/P2P/Payloads/Witness.cs | 4 +--- neo/SmartContract/Helper.cs | 13 ++++++++++-- neo/SmartContract/InteropService.NEO.cs | 4 ++-- neo/SmartContract/WitnessWrapper.cs | 27 +++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 neo/SmartContract/WitnessWrapper.cs diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index c8625a4e04..9bf06acbf7 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -92,7 +92,7 @@ public static Transaction CreateRandomHashTransaction() new Witness { InvocationScript = new byte[0], - VerificationScript = new byte[1] + VerificationScript = new byte[0] } } }; diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index b02d521dcc..237a954642 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.SmartContract; -using System; +using Neo.VM; using System.IO; namespace Neo.Network.P2P.Payloads @@ -30,8 +30,6 @@ void ISerializable.Deserialize(BinaryReader reader) { InvocationScript = reader.ReadVarBytes(65536); VerificationScript = reader.ReadVarBytes(65536); - if (VerificationScript.Length == 0) - throw new FormatException(); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 3cf40a42d8..f40dad212e 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -262,10 +262,19 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snaps if (hashes.Length != verifiable.Witnesses.Length) return false; for (int i = 0; i < hashes.Length; i++) { - if (hashes[i] != verifiable.Witnesses[i].ScriptHash) 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(verifiable.Witnesses[i].VerificationScript); + 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; diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 812a2d7155..5787cebdb4 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -214,7 +214,7 @@ private static bool Transaction_GetWitnesses(ApplicationEngine engine) if (tx == null) return false; if (tx.Witnesses.Length > engine.MaxArraySize) return false; - engine.CurrentContext.EvaluationStack.Push(tx.Witnesses.Select(p => StackItem.FromInterface(p)).ToArray()); + engine.CurrentContext.EvaluationStack.Push(WitnessWrapper.Create(tx, engine.Snapshot).Select(p => StackItem.FromInterface(p)).ToArray()); return true; } return false; @@ -224,7 +224,7 @@ private static bool Witness_GetVerificationScript(ApplicationEngine engine) { if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { - Witness witness = _interface.GetInterface(); + WitnessWrapper witness = _interface.GetInterface(); if (witness == null) return false; engine.CurrentContext.EvaluationStack.Push(witness.VerificationScript); return true; 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 94c4579f8f3cff69399746c7397e9dd57681cd24 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 25 Jun 2019 11:02:22 +0800 Subject: [PATCH 3/6] Simplify re-verification --- neo.UnitTests/UT_MemoryPool.cs | 1 + neo/Ledger/MemoryPool.cs | 2 +- neo/Network/P2P/Payloads/Transaction.cs | 30 +++++++++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 1beabc1d65..ee79d96842 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -48,6 +48,7 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny>())).Returns(true); mock.Setup(p => p.Verify(It.IsAny(), It.IsAny>())).Returns(true); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index f4fee9bdf2..1c308019ca 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -388,7 +388,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, // Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end. foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { - if (item.Tx.Verify(snapshot, _unsortedTransactions.Select(p => p.Value.Tx))) + if (item.Tx.Reverify(snapshot, _unsortedTransactions.Select(p => p.Value.Tx))) reverifiedItems.Add(item); else // Transaction no longer valid -- it will be removed from unverifiedTxPool. invalidItems.Add(item); diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 152ac12b59..923fe6447f 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -122,6 +122,26 @@ public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) return hashes.OrderBy(p => p).ToArray(); } + public virtual bool Reverify(Snapshot snapshot, IEnumerable mempool) + { + if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) + return false; + if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) + return false; + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); + BigInteger fee = SystemFee + NetworkFee; + if (balance < fee) return false; + fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); + if (balance < fee) return false; + UInt160[] hashes = GetScriptHashesForVerifying(snapshot); + for (int i = 0; i < hashes.Length; i++) + { + if (Witnesses[i].VerificationScript.Length > 0) continue; + if (snapshot.Contracts.TryGet(hashes[i]) is null) return false; + } + return true; + } + void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); @@ -164,19 +184,11 @@ bool IInventory.Verify(Snapshot snapshot) public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) { - if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) - return false; + if (!Reverify(snapshot, mempool)) return false; int size = Size; if (size > MaxTransactionSize) 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 = SystemFee + NetworkFee; - if (balance < fee) return false; - 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); } } From bcdc4355ab700fe08f386c3657a71b7f59bb17ae Mon Sep 17 00:00:00 2001 From: erikzhang Date: Mon, 1 Jul 2019 16:01:42 +0800 Subject: [PATCH 4/6] Clear mempool when policy is changed --- neo/Ledger/MemoryPool.cs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 1c308019ca..3affde9016 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -61,6 +61,8 @@ public class MemoryPool : IReadOnlyCollection internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; private int _maxTxPerBlock; + private long _feePerByte; + private bool _policyChanged; /// /// Total maximum capacity of transactions the pool can hold. @@ -102,6 +104,9 @@ public MemoryPool(NeoSystem system, int capacity) internal void LoadPolicy(Snapshot snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); + long newFeePerByte = NativeContract.Policy.GetFeePerByte(snapshot); + _policyChanged = newFeePerByte > _feePerByte; + _feePerByte = newFeePerByte; } /// @@ -337,18 +342,30 @@ internal void InvalidateVerifiedTransactions() // Note: this must only be called from a single thread (the Blockchain actor) internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) { + LoadPolicy(snapshot); + _txRwLock.EnterWriteLock(); try { - // First remove the transactions verified in the block. - foreach (Transaction tx in block.Transactions) + if (_policyChanged) { - if (TryRemoveVerified(tx.Hash, out _)) continue; - TryRemoveUnVerified(tx.Hash, out _); + _unsortedTransactions.Clear(); + _sortedTransactions.Clear(); + _unverifiedTransactions.Clear(); + _unverifiedSortedTransactions.Clear(); } + else + { + // First remove the transactions verified in the block. + foreach (Transaction tx in block.Transactions) + { + if (TryRemoveVerified(tx.Hash, out _)) continue; + TryRemoveUnVerified(tx.Hash, out _); + } - // Add all the previously verified transactions back to the unverified transactions - InvalidateVerifiedTransactions(); + // Add all the previously verified transactions back to the unverified transactions + InvalidateVerifiedTransactions(); + } } finally { @@ -357,10 +374,9 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) // If we know about headers of future blocks, no point in verifying transactions from the unverified tx pool // until we get caught up. - if (block.Index > 0 && block.Index < Blockchain.Singleton.HeaderHeight) + if (block.Index > 0 && block.Index < Blockchain.Singleton.HeaderHeight || _policyChanged) return; - LoadPolicy(snapshot); ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, _maxTxPerBlock, MaxSecondsToReverifyTx, snapshot); } From e80bb7207e4f0df3524758486f3fc4f3a2f9fe38 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 2 Jul 2019 04:29:13 +0800 Subject: [PATCH 5/6] put _policyChanged in local scope --- neo/Ledger/MemoryPool.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 3affde9016..6ff6b01bdb 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -62,7 +62,6 @@ public class MemoryPool : IReadOnlyCollection private int _maxTxPerBlock; private long _feePerByte; - private bool _policyChanged; /// /// Total maximum capacity of transactions the pool can hold. @@ -101,12 +100,13 @@ public MemoryPool(NeoSystem system, int capacity) Capacity = capacity; } - internal void LoadPolicy(Snapshot snapshot) + internal bool LoadPolicy(Snapshot snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); long newFeePerByte = NativeContract.Policy.GetFeePerByte(snapshot); - _policyChanged = newFeePerByte > _feePerByte; + bool policyChanged = newFeePerByte > _feePerByte; _feePerByte = newFeePerByte; + return policyChanged; } /// @@ -342,12 +342,12 @@ internal void InvalidateVerifiedTransactions() // Note: this must only be called from a single thread (the Blockchain actor) internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) { - LoadPolicy(snapshot); + bool policyChanged = LoadPolicy(snapshot); _txRwLock.EnterWriteLock(); try { - if (_policyChanged) + if (policyChanged) { _unsortedTransactions.Clear(); _sortedTransactions.Clear(); @@ -374,7 +374,7 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) // If we know about headers of future blocks, no point in verifying transactions from the unverified tx pool // until we get caught up. - if (block.Index > 0 && block.Index < Blockchain.Singleton.HeaderHeight || _policyChanged) + if (block.Index > 0 && block.Index < Blockchain.Singleton.HeaderHeight || policyChanged) return; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, From d829748158d2cac65bbff827fba38f94490b2ebb Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 4 Jul 2019 12:27:30 +0800 Subject: [PATCH 6/6] Reverify transactions when policy changes --- neo/Ledger/MemoryPool.cs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 6ff6b01bdb..94f7ec9e0f 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -1,3 +1,4 @@ +using Akka.Actor; using Akka.Util.Internal; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; @@ -347,25 +348,23 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) _txRwLock.EnterWriteLock(); try { + // First remove the transactions verified in the block. + foreach (Transaction tx in block.Transactions) + { + if (TryRemoveVerified(tx.Hash, out _)) continue; + TryRemoveUnVerified(tx.Hash, out _); + } + + // Add all the previously verified transactions back to the unverified transactions + InvalidateVerifiedTransactions(); + if (policyChanged) { - _unsortedTransactions.Clear(); - _sortedTransactions.Clear(); + foreach (PoolItem item in _unverifiedSortedTransactions.Reverse()) + _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); _unverifiedTransactions.Clear(); _unverifiedSortedTransactions.Clear(); } - else - { - // First remove the transactions verified in the block. - foreach (Transaction tx in block.Transactions) - { - if (TryRemoveVerified(tx.Hash, out _)) continue; - TryRemoveUnVerified(tx.Hash, out _); - } - - // Add all the previously verified transactions back to the unverified transactions - InvalidateVerifiedTransactions(); - } } finally {