diff --git a/neo/Core/Helper.cs b/neo/Core/Helper.cs index adbe74dbbb..f21b6c02a5 100644 --- a/neo/Core/Helper.cs +++ b/neo/Core/Helper.cs @@ -70,7 +70,7 @@ internal static bool VerifyScripts(this IVerifiable verifiable) { if (hashes[i] != verifiable.Scripts[i].ScriptHash) return false; } - ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, Blockchain.Default, StateReader.Default, Fixed8.Zero); + ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, Blockchain.Default, new StateReader(), Fixed8.Zero); engine.LoadScript(verification, false); engine.LoadScript(verifiable.Scripts[i].InvocationScript, true); if (!engine.Execute()) return false; diff --git a/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs index 2968da31df..c2c63ee579 100644 --- a/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs +++ b/neo/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using Iterator = Neo.IO.Data.LevelDB.Iterator; namespace Neo.Implementations.Blockchains.LevelDB { diff --git a/neo/SmartContract/Iterator.cs b/neo/SmartContract/Iterator.cs new file mode 100644 index 0000000000..ad78c27bff --- /dev/null +++ b/neo/SmartContract/Iterator.cs @@ -0,0 +1,13 @@ +using Neo.VM; +using System; + +namespace Neo.SmartContract +{ + internal abstract class Iterator : IDisposable, IInteropInterface + { + public abstract void Dispose(); + public abstract StackItem Key(); + public abstract bool Next(); + public abstract StackItem Value(); + } +} diff --git a/neo/SmartContract/StateMachine.cs b/neo/SmartContract/StateMachine.cs index f2137e4c03..a21a9624b8 100644 --- a/neo/SmartContract/StateMachine.cs +++ b/neo/SmartContract/StateMachine.cs @@ -19,9 +19,11 @@ public class StateMachine : StateReader private readonly DataCache storages; private Dictionary contracts_created = new Dictionary(); - private List notifications = new List(); - public IReadOnlyList Notifications => notifications; + protected override DataCache Accounts => accounts; + protected override DataCache Assets => assets; + protected override DataCache Contracts => contracts; + protected override DataCache Storages => storages; public StateMachine(Block persisting_block, DataCache accounts, DataCache assets, DataCache contracts, DataCache storages) { @@ -30,7 +32,6 @@ public StateMachine(Block persisting_block, DataCache acc this.assets = assets.CreateSnapshot(); this.contracts = contracts.CreateSnapshot(); this.storages = storages.CreateSnapshot(); - Notify += StateMachine_Notify; Register("Neo.Asset.Create", Asset_Create); Register("Neo.Asset.Renew", Asset_Renew); Register("Neo.Contract.Create", Contract_Create); @@ -51,14 +52,6 @@ public StateMachine(Block persisting_block, DataCache acc #endregion } - private bool CheckStorageContext(StorageContext context) - { - ContractState contract = contracts.TryGet(context.ScriptHash); - if (contract == null) return false; - if (!contract.HasStorage) return false; - return true; - } - public void Commit() { accounts.Commit(); @@ -67,42 +60,12 @@ public void Commit() storages.Commit(); } - private void StateMachine_Notify(object sender, NotifyEventArgs e) - { - notifications.Add(e); - } - protected override bool Runtime_GetTime(ExecutionEngine engine) { engine.EvaluationStack.Push(persisting_block.Timestamp); return true; } - protected override bool Blockchain_GetAccount(ExecutionEngine engine) - { - UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); - engine.EvaluationStack.Push(StackItem.FromInterface(accounts[hash])); - return true; - } - - protected override bool Blockchain_GetAsset(ExecutionEngine engine) - { - UInt256 hash = new UInt256(engine.EvaluationStack.Pop().GetByteArray()); - AssetState asset = assets.TryGet(hash); - if (asset == null) return false; - engine.EvaluationStack.Push(StackItem.FromInterface(asset)); - return true; - } - - protected override bool Blockchain_GetContract(ExecutionEngine engine) - { - UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); - ContractState contract = contracts.TryGet(hash); - if (contract == null) return false; - engine.EvaluationStack.Push(StackItem.FromInterface(contract)); - return true; - } - private bool Asset_Create(ExecutionEngine engine) { InvocationTransaction tx = (InvocationTransaction)engine.ScriptContainer; @@ -295,24 +258,6 @@ private bool Contract_Destroy(ExecutionEngine engine) return true; } - protected override bool Storage_Get(ExecutionEngine engine) - { - if (engine.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (!CheckStorageContext(context)) return false; - byte[] key = engine.EvaluationStack.Pop().GetByteArray(); - StorageItem item = storages.TryGet(new StorageKey - { - ScriptHash = context.ScriptHash, - Key = key - }); - engine.EvaluationStack.Push(item?.Value ?? new byte[0]); - return true; - } - return false; - } - private bool Storage_Put(ExecutionEngine engine) { if (engine.EvaluationStack.Pop() is InteropInterface _interface) diff --git a/neo/SmartContract/StateReader.cs b/neo/SmartContract/StateReader.cs index dc74488d59..f10aca0c53 100644 --- a/neo/SmartContract/StateReader.cs +++ b/neo/SmartContract/StateReader.cs @@ -1,9 +1,11 @@ using Neo.Core; using Neo.Cryptography.ECC; using Neo.IO; +using Neo.IO.Caching; using Neo.VM; using Neo.VM.Types; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; @@ -13,12 +15,59 @@ namespace Neo.SmartContract { - public class StateReader : InteropService + public class StateReader : InteropService, IDisposable { - public event EventHandler Notify; - public event EventHandler Log; + public static event EventHandler Notify; + public static event EventHandler Log; - public static readonly StateReader Default = new StateReader(); + private readonly List notifications = new List(); + private readonly List disposables = new List(); + + public IReadOnlyList Notifications => notifications; + + private DataCache _accounts; + protected virtual DataCache Accounts + { + get + { + if (_accounts == null) + _accounts = Blockchain.Default.GetStates(); + return _accounts; + } + } + + private DataCache _assets; + protected virtual DataCache Assets + { + get + { + if (_assets == null) + _assets = Blockchain.Default.GetStates(); + return _assets; + } + } + + private DataCache _contracts; + protected virtual DataCache Contracts + { + get + { + if (_contracts == null) + _contracts = Blockchain.Default.GetStates(); + return _contracts; + } + } + + private DataCache _storages; + protected virtual DataCache Storages + { + get + { + if (_storages == null) + _storages = Blockchain.Default.GetStates(); + return _storages; + } + } public StateReader() { @@ -76,6 +125,10 @@ public StateReader() Register("Neo.Contract.GetScript", Contract_GetScript); Register("Neo.Storage.GetContext", Storage_GetContext); Register("Neo.Storage.Get", Storage_Get); + Register("Neo.Storage.Find", Storage_Find); + Register("Neo.Iterator.Next", Iterator_Next); + Register("Neo.Iterator.Key", Iterator_Key); + Register("Neo.Iterator.Value", Iterator_Value); #region Old AntShares APIs Register("AntShares.Runtime.CheckWitness", Runtime_CheckWitness); Register("AntShares.Runtime.Notify", Runtime_Notify); @@ -128,6 +181,21 @@ public StateReader() #endregion } + internal bool CheckStorageContext(StorageContext context) + { + ContractState contract = Contracts.TryGet(context.ScriptHash); + if (contract == null) return false; + if (!contract.HasStorage) return false; + return true; + } + + public void Dispose() + { + foreach (IDisposable disposable in disposables) + disposable.Dispose(); + disposables.Clear(); + } + protected virtual bool Runtime_GetTrigger(ExecutionEngine engine) { ApplicationEngine app_engine = (ApplicationEngine)engine; @@ -164,7 +232,9 @@ protected virtual bool Runtime_CheckWitness(ExecutionEngine engine) protected virtual bool Runtime_Notify(ExecutionEngine engine) { StackItem state = engine.EvaluationStack.Pop(); - Notify?.Invoke(this, new NotifyEventArgs(engine.ScriptContainer, new UInt160(engine.CurrentContext.ScriptHash), state)); + NotifyEventArgs notification = new NotifyEventArgs(engine.ScriptContainer, new UInt160(engine.CurrentContext.ScriptHash), state); + Notify?.Invoke(this, notification); + notifications.Add(notification); return true; } @@ -351,8 +421,8 @@ protected virtual bool Blockchain_GetTransaction(ExecutionEngine engine) protected virtual bool Blockchain_GetAccount(ExecutionEngine engine) { - byte[] hash = engine.EvaluationStack.Pop().GetByteArray(); - AccountState account = Blockchain.Default?.GetAccountState(new UInt160(hash)); + UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); + AccountState account = Accounts.GetOrAdd(hash, () => new AccountState(hash)); engine.EvaluationStack.Push(StackItem.FromInterface(account)); return true; } @@ -366,8 +436,9 @@ protected virtual bool Blockchain_GetValidators(ExecutionEngine engine) protected virtual bool Blockchain_GetAsset(ExecutionEngine engine) { - byte[] hash = engine.EvaluationStack.Pop().GetByteArray(); - AssetState asset = Blockchain.Default?.GetAssetState(new UInt256(hash)); + UInt256 hash = new UInt256(engine.EvaluationStack.Pop().GetByteArray()); + AssetState asset = Assets.TryGet(hash); + if (asset == null) return false; engine.EvaluationStack.Push(StackItem.FromInterface(asset)); return true; } @@ -375,7 +446,7 @@ protected virtual bool Blockchain_GetAsset(ExecutionEngine engine) protected virtual bool Blockchain_GetContract(ExecutionEngine engine) { UInt160 hash = new UInt160(engine.EvaluationStack.Pop().GetByteArray()); - ContractState contract = Blockchain.Default.GetContract(hash); + ContractState contract = Contracts.TryGet(hash); if (contract == null) return false; engine.EvaluationStack.Push(StackItem.FromInterface(contract)); return true; @@ -844,11 +915,9 @@ protected virtual bool Storage_Get(ExecutionEngine engine) if (engine.EvaluationStack.Pop() is InteropInterface _interface) { StorageContext context = _interface.GetInterface(); - ContractState contract = Blockchain.Default.GetContract(context.ScriptHash); - if (contract == null) return false; - if (!contract.HasStorage) return false; + if (!CheckStorageContext(context)) return false; byte[] key = engine.EvaluationStack.Pop().GetByteArray(); - StorageItem item = Blockchain.Default.GetStorageItem(new StorageKey + StorageItem item = Storages.TryGet(new StorageKey { ScriptHash = context.ScriptHash, Key = key @@ -858,5 +927,54 @@ protected virtual bool Storage_Get(ExecutionEngine engine) } return false; } + + protected virtual bool Storage_Find(ExecutionEngine engine) + { + if (engine.EvaluationStack.Pop() is InteropInterface _interface) + { + StorageContext context = _interface.GetInterface(); + if (!CheckStorageContext(context)) return false; + byte[] prefix = engine.EvaluationStack.Pop().GetByteArray(); + prefix = context.ScriptHash.ToArray().Concat(prefix).ToArray(); + StorageIterator iterator = new StorageIterator(Storages.Find(prefix).GetEnumerator()); + engine.EvaluationStack.Push(StackItem.FromInterface(iterator)); + disposables.Add(iterator); + return true; + } + return false; + } + + protected virtual bool Iterator_Next(ExecutionEngine engine) + { + if (engine.EvaluationStack.Pop() is InteropInterface _interface) + { + Iterator iterator = _interface.GetInterface(); + engine.EvaluationStack.Push(iterator.Next()); + return true; + } + return false; + } + + protected virtual bool Iterator_Key(ExecutionEngine engine) + { + if (engine.EvaluationStack.Pop() is InteropInterface _interface) + { + Iterator iterator = _interface.GetInterface(); + engine.EvaluationStack.Push(iterator.Key()); + return true; + } + return false; + } + + protected virtual bool Iterator_Value(ExecutionEngine engine) + { + if (engine.EvaluationStack.Pop() is InteropInterface _interface) + { + Iterator iterator = _interface.GetInterface(); + engine.EvaluationStack.Push(iterator.Value()); + return true; + } + return false; + } } } diff --git a/neo/SmartContract/StorageIterator.cs b/neo/SmartContract/StorageIterator.cs new file mode 100644 index 0000000000..0aa5d4873d --- /dev/null +++ b/neo/SmartContract/StorageIterator.cs @@ -0,0 +1,36 @@ +using Neo.Core; +using Neo.VM; +using System.Collections.Generic; + +namespace Neo.SmartContract +{ + internal class StorageIterator : Iterator + { + private readonly IEnumerator> enumerator; + + public StorageIterator(IEnumerator> enumerator) + { + this.enumerator = enumerator; + } + + public override void Dispose() + { + enumerator.Dispose(); + } + + public override StackItem Key() + { + return enumerator.Current.Key.Key; + } + + public override bool Next() + { + return enumerator.MoveNext(); + } + + public override StackItem Value() + { + return enumerator.Current.Value.Value; + } + } +}