Skip to content

Commit

Permalink
add Iterator APIs and StorageIterator
Browse files Browse the repository at this point in the history
1. Add `Neo.Iterator.*`
1. Add `Neo.Storage.Find`
  • Loading branch information
Erik Zhang committed Feb 5, 2018
1 parent f0cf5cc commit a234978
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 74 deletions.
2 changes: 1 addition & 1 deletion neo/Core/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
13 changes: 13 additions & 0 deletions neo/SmartContract/Iterator.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
63 changes: 4 additions & 59 deletions neo/SmartContract/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ public class StateMachine : StateReader
private readonly DataCache<StorageKey, StorageItem> storages;

private Dictionary<UInt160, UInt160> contracts_created = new Dictionary<UInt160, UInt160>();
private List<NotifyEventArgs> notifications = new List<NotifyEventArgs>();

public IReadOnlyList<NotifyEventArgs> Notifications => notifications;
protected override DataCache<UInt160, AccountState> Accounts => accounts;
protected override DataCache<UInt256, AssetState> Assets => assets;
protected override DataCache<UInt160, ContractState> Contracts => contracts;
protected override DataCache<StorageKey, StorageItem> Storages => storages;

public StateMachine(Block persisting_block, DataCache<UInt160, AccountState> accounts, DataCache<UInt256, AssetState> assets, DataCache<UInt160, ContractState> contracts, DataCache<StorageKey, StorageItem> storages)
{
Expand All @@ -30,7 +32,6 @@ public StateMachine(Block persisting_block, DataCache<UInt160, AccountState> 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);
Expand All @@ -51,14 +52,6 @@ public StateMachine(Block persisting_block, DataCache<UInt160, AccountState> 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();
Expand All @@ -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;
Expand Down Expand Up @@ -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<StorageContext>();
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)
Expand Down
146 changes: 132 additions & 14 deletions neo/SmartContract/StateReader.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,12 +15,59 @@

namespace Neo.SmartContract
{
public class StateReader : InteropService
public class StateReader : InteropService, IDisposable
{
public event EventHandler<NotifyEventArgs> Notify;
public event EventHandler<LogEventArgs> Log;
public static event EventHandler<NotifyEventArgs> Notify;
public static event EventHandler<LogEventArgs> Log;

public static readonly StateReader Default = new StateReader();
private readonly List<NotifyEventArgs> notifications = new List<NotifyEventArgs>();
private readonly List<IDisposable> disposables = new List<IDisposable>();

public IReadOnlyList<NotifyEventArgs> Notifications => notifications;

private DataCache<UInt160, AccountState> _accounts;
protected virtual DataCache<UInt160, AccountState> Accounts
{
get
{
if (_accounts == null)
_accounts = Blockchain.Default.GetStates<UInt160, AccountState>();
return _accounts;
}
}

private DataCache<UInt256, AssetState> _assets;
protected virtual DataCache<UInt256, AssetState> Assets
{
get
{
if (_assets == null)
_assets = Blockchain.Default.GetStates<UInt256, AssetState>();
return _assets;
}
}

private DataCache<UInt160, ContractState> _contracts;
protected virtual DataCache<UInt160, ContractState> Contracts
{
get
{
if (_contracts == null)
_contracts = Blockchain.Default.GetStates<UInt160, ContractState>();
return _contracts;
}
}

private DataCache<StorageKey, StorageItem> _storages;
protected virtual DataCache<StorageKey, StorageItem> Storages
{
get
{
if (_storages == null)
_storages = Blockchain.Default.GetStates<StorageKey, StorageItem>();
return _storages;
}
}

public StateReader()
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -366,16 +436,17 @@ 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;
}

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;
Expand Down Expand Up @@ -844,11 +915,9 @@ protected virtual bool Storage_Get(ExecutionEngine engine)
if (engine.EvaluationStack.Pop() is InteropInterface _interface)
{
StorageContext context = _interface.GetInterface<StorageContext>();
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
Expand All @@ -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<StorageContext>();
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<Iterator>();
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<Iterator>();
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<Iterator>();
engine.EvaluationStack.Push(iterator.Value());
return true;
}
return false;
}
}
}
36 changes: 36 additions & 0 deletions neo/SmartContract/StorageIterator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Neo.Core;
using Neo.VM;
using System.Collections.Generic;

namespace Neo.SmartContract
{
internal class StorageIterator : Iterator
{
private readonly IEnumerator<KeyValuePair<StorageKey, StorageItem>> enumerator;

public StorageIterator(IEnumerator<KeyValuePair<StorageKey, StorageItem>> 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;
}
}
}

0 comments on commit a234978

Please sign in to comment.