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..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; @@ -61,6 +62,7 @@ public class MemoryPool : IReadOnlyCollection internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; private int _maxTxPerBlock; + private long _feePerByte; /// /// Total maximum capacity of transactions the pool can hold. @@ -99,9 +101,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); + bool policyChanged = newFeePerByte > _feePerByte; + _feePerByte = newFeePerByte; + return policyChanged; } /// @@ -337,6 +343,8 @@ internal void InvalidateVerifiedTransactions() // Note: this must only be called from a single thread (the Blockchain actor) internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) { + bool policyChanged = LoadPolicy(snapshot); + _txRwLock.EnterWriteLock(); try { @@ -349,6 +357,14 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) // Add all the previously verified transactions back to the unverified transactions InvalidateVerifiedTransactions(); + + if (policyChanged) + { + foreach (PoolItem item in _unverifiedSortedTransactions.Reverse()) + _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); + _unverifiedTransactions.Clear(); + _unverifiedSortedTransactions.Clear(); + } } finally { @@ -357,10 +373,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); } @@ -388,7 +403,7 @@ internal void InvalidateAllTransactions() // 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); } } 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..5787cebdb4 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) { @@ -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 77a17369fa..669a937885 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());