Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt to Script Hash Identification Change #90

Merged
merged 10 commits into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 33 additions & 20 deletions src/neo3/BlockchainOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -418,16 +418,7 @@ static async Task<UInt160> ParseAssetAsync(Node.IExpressNode expressNode, string
return uint160;
}

if (asset.EndsWith(".nef"))
{
var parser = new ContractParameterParser();
if (parser.TryLoadScriptHash(asset, System.Environment.CurrentDirectory, out var scriptHash))
{
return scriptHash;
}
}

var contracts = await expressNode.ListNep5ContractsAsync().ConfigureAwait(false);
var contracts = await expressNode.ListNep17ContractsAsync().ConfigureAwait(false);
for (int i = 0; i < contracts.Count; i++)
{
if (contracts[i].Symbol.Equals(asset, StringComparison.CurrentCultureIgnoreCase))
Expand All @@ -453,7 +444,7 @@ public async Task<UInt256> DeployContract(ExpressChain chain, string contract, E
var (nefFile, manifest) = await LoadContract(contract).ConfigureAwait(false);

using var sb = new ScriptBuilder();
sb.EmitSysCall(ApplicationEngine.System_Contract_Create, nefFile.Script, manifest.ToString());
sb.EmitSysCall(ApplicationEngine.System_Contract_Create, nefFile.ToArray(), manifest.ToString());
return await expressNode.ExecuteAsync(chain, account, sb.ToArray()).ConfigureAwait(false);

static async Task<(NefFile nefFile, ContractManifest manifest)> LoadContract(string contractPath)
Expand Down Expand Up @@ -481,7 +472,8 @@ public async Task<UInt256> InvokeContract(ExpressChain chain, string invocationF
}

using var expressNode = chain.GetExpressNode(trace);
var parser = GetContractParameterParser(chain);
var contracts = await expressNode.ListContractsAsync().ConfigureAwait(false);
var parser = GetContractParameterParser(chain, contracts);
var script = await parser.LoadInvocationScriptAsync(invocationFilePath).ConfigureAwait(false);
return await expressNode.ExecuteAsync(chain, account, script, additionalGas).ConfigureAwait(false);
}
Expand All @@ -494,14 +486,15 @@ public async Task<InvokeResult> TestInvokeContract(ExpressChain chain, string in
}

using var expressNode = chain.GetExpressNode();
var parser = GetContractParameterParser(chain);
var contracts = await expressNode.ListContractsAsync().ConfigureAwait(false);
var parser = GetContractParameterParser(chain, contracts);
var script = await parser.LoadInvocationScriptAsync(invocationFilePath).ConfigureAwait(false);
return await expressNode.InvokeAsync(script).ConfigureAwait(false);
}

ContractParameterParser GetContractParameterParser(ExpressChain chain)
ContractParameterParser GetContractParameterParser(ExpressChain chain, IReadOnlyList<(UInt160 hash, ContractManifest manifest)> contracts)
{
ContractParameterParser.TryGetAccount tryGetAccount = (string name, out UInt160 scriptHash) =>
ContractParameterParser.TryGetUInt160 tryGetAccount = (string name, out UInt160 scriptHash) =>
{
var account = GetAccount(chain, name);
if (account != null)
Expand All @@ -514,7 +507,27 @@ ContractParameterParser GetContractParameterParser(ExpressChain chain)
return false;
};

return new ContractParameterParser(new System.IO.Abstractions.FileSystem(), tryGetAccount);
var lookup = contracts.ToDictionary(c => c.manifest.Name, c => c.hash);
ContractParameterParser.TryGetUInt160 tryGetContract = (string name, out UInt160 scriptHash) =>
{
if (lookup.TryGetValue(name, out scriptHash))
{
return true;
}

foreach (var kvp in lookup)
{
if (string.Equals(name, kvp.Key, StringComparison.OrdinalIgnoreCase))
{
scriptHash = kvp.Value;
return true;
}
}

return false;
};

return new ContractParameterParser(tryGetAccount, tryGetContract);
}

public ExpressWalletAccount? GetAccount(ExpressChain chain, string name)
Expand Down Expand Up @@ -543,7 +556,7 @@ ContractParameterParser GetContractParameterParser(ExpressChain chain)
return null;
}

public async Task<(BigDecimal balance, Nep5Contract contract)> ShowBalance(ExpressChain chain, ExpressWalletAccount account, string asset)
public async Task<(BigDecimal balance, Nep17Contract contract)> ShowBalance(ExpressChain chain, ExpressWalletAccount account, string asset)
{
if (!NodeUtility.InitializeProtocolSettings(chain))
{
Expand All @@ -569,13 +582,13 @@ ContractParameterParser GetContractParameterParser(ExpressChain chain)
var symbol = Encoding.UTF8.GetString(stack[2].GetSpan());
var decimals = (byte)(stack[3].GetInteger());

return (new BigDecimal(balance, decimals), new Nep5Contract(name, symbol, decimals, assetHash));
return (new BigDecimal(balance, decimals), new Nep17Contract(name, symbol, decimals, assetHash));
}

throw new Exception("invalid script results");
}

public async Task<(RpcNep5Balance balance, Nep5Contract contract)[]> GetBalances(ExpressChain chain, ExpressWalletAccount account)
public async Task<(RpcNep17Balance balance, Nep17Contract contract)[]> GetBalances(ExpressChain chain, ExpressWalletAccount account)
{
if (!NodeUtility.InitializeProtocolSettings(chain))
{
Expand Down Expand Up @@ -650,7 +663,7 @@ public async Task<ContractManifest> GetContract(ExpressChain chain, string hashO
return await expressNode.GetContractAsync(scriptHash);
}

public async Task<IReadOnlyList<ContractManifest>> ListContracts(ExpressChain chain)
public async Task<IReadOnlyList<(UInt160 hash, ContractManifest manifest)>> ListContracts(ExpressChain chain)
{
if (!NodeUtility.InitializeProtocolSettings(chain))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@

namespace NeoExpress.Neo3.Models
{
public struct Nep5Contract
public struct Nep17Contract
{
public readonly string Name;
public readonly string Symbol;
public readonly byte Decimals;
public readonly UInt160 ScriptHash;

public Nep5Contract(string name, string symbol, byte decimals, UInt160 scriptHash)
public Nep17Contract(string name, string symbol, byte decimals, UInt160 scriptHash)
{
Name = name;
Symbol = symbol;
Decimals = decimals;
ScriptHash = scriptHash;
}

public static Nep5Contract Unknown(UInt160 scriptHash) => new Nep5Contract("unknown", "unknown", 0, scriptHash);
public static Nep17Contract Unknown(UInt160 scriptHash) => new Nep17Contract("unknown", "unknown", 0, scriptHash);

public static bool TryLoad(IReadOnlyStore store, UInt160 scriptHash, out Nep5Contract contract)
public static bool TryLoad(IReadOnlyStore store, UInt160 scriptHash, out Nep17Contract contract)
{
using var sb = new ScriptBuilder();
sb.EmitAppCall(scriptHash, "name");
Expand All @@ -36,7 +36,7 @@ public static bool TryLoad(IReadOnlyStore store, UInt160 scriptHash, out Nep5Con
var decimals = (byte)engine.ResultStack.Pop<Neo.VM.Types.Integer>().GetInteger();
var symbol = Encoding.UTF8.GetString(engine.ResultStack.Pop().GetSpan());
var name = Encoding.UTF8.GetString(engine.ResultStack.Pop().GetSpan());
contract = new Nep5Contract(name, symbol, decimals, scriptHash);
contract = new Nep17Contract(name, symbol, decimals, scriptHash);
return true;
}

Expand All @@ -54,13 +54,13 @@ public JObject ToJson()
return json;
}

public static Nep5Contract FromJson(JObject json)
public static Nep17Contract FromJson(JObject json)
{
var name = json["name"].AsString();
var symbol = json["symbol"].AsString();
var scriptHash = UInt160.Parse(json["scriptHash"].AsString());
var decimals = (byte)json["decimals"].AsNumber();
return new Nep5Contract(name, symbol, decimals, scriptHash);
return new Nep17Contract(name, symbol, decimals, scriptHash);
}
}
}
Expand Down
27 changes: 21 additions & 6 deletions src/neo3/Node/ExpressApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ namespace NeoExpress.Neo3.Node
{
internal class ExpressApplicationEngine : ApplicationEngine
{
// In next preview, Execute method will be virtual so it will be easier to
// retrieve start/end state. In the meantime, preExecTrace and completeStateChange
// are used to control tracing of start/end states.

private readonly ITraceDebugSink traceDebugSink;
private readonly StoreView snapshot;
private readonly Dictionary<UInt160, string> contractNameMap = new Dictionary<UInt160, string>();

public ExpressApplicationEngine(ITraceDebugSink traceDebugSink, TriggerType trigger, IVerifiable container, StoreView snapshot, long gas)
: base(trigger, container, snapshot, gas)
{
this.traceDebugSink = traceDebugSink;
this.snapshot = snapshot;

Log += OnLog;
Notify += OnNotify;
}
Expand All @@ -35,19 +35,34 @@ public override void Dispose()
base.Dispose();
}

private string GetContractName(UInt160 scriptId)
{
if (contractNameMap.TryGetValue(scriptId, out var name))
{
return name;
}

var state = snapshot.Contracts.TryGet(scriptId);
name = state != null ? state.Manifest.Name : "";
contractNameMap[scriptId] = name;
return name;
}

private void OnNotify(object sender, NotifyEventArgs args)
{
if (ReferenceEquals(sender, this))
{
traceDebugSink.Notify(args);
var name = GetContractName(args.ScriptHash);
traceDebugSink.Notify(args, name);
}
}

private void OnLog(object sender, LogEventArgs args)
{
if (ReferenceEquals(sender, this))
{
traceDebugSink.Log(args);
var name = GetContractName(args.ScriptHash);
traceDebugSink.Log(args, name);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/neo3/Node/ExpressApplicationEngineProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace NeoExpress.Neo3.Node
{
// Note: namespace alias needed to avoid conflict with Plugin.System property
using SysIO = System.IO;

class ExpressApplicationEngineProvider : Plugin, IApplicationEngineProvider
Expand Down
2 changes: 1 addition & 1 deletion src/neo3/Node/ExpressOracle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class ExpressOracle
// Calculate network fee

var engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.Clone());
engine.LoadScript(NativeContract.Oracle.Script, CallFlags.None, 0);
engine.LoadScript(NativeContract.Oracle.Script, CallFlags.None);
engine.LoadScript(new ScriptBuilder().Emit(OpCode.DEPTH, OpCode.PACK).EmitPush("verify").ToArray(), CallFlags.None);
if (engine.Execute() != VMState.HALT) return null;
tx.NetworkFee += engine.GasConsumed;
Expand Down
40 changes: 22 additions & 18 deletions src/neo3/Node/ExpressRpcServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,29 +80,29 @@ public JObject GetApplicationLog(JArray _params)

// TODO: should the event name comparison be case insensitive?
// Native contracts use "Transfer" while neon preview 3 compiled contracts use "transfer"
static bool IsNep5Transfer(NotificationRecord notification)
static bool IsNep17Transfer(NotificationRecord notification)
=> notification.InventoryType == InventoryType.TX
&& notification.State.Count == 3
&& notification.State.Count == 4
&& (notification.EventName == "Transfer" || notification.EventName == "transfer");

static IEnumerable<(uint blockIndex, ushort txIndex, NotificationRecord notification)> GetNep5Transfers(IReadOnlyStore store)
static IEnumerable<(uint blockIndex, ushort txIndex, NotificationRecord notification)> GetNep17Transfers(IReadOnlyStore store)
{
return ExpressAppLogsPlugin
.GetNotifications(store)
.Where(t => IsNep5Transfer(t.notification));
.Where(t => IsNep17Transfer(t.notification));
}

public static IEnumerable<Nep5Contract> GetNep5Contracts(IReadOnlyStore store)
public static IEnumerable<Nep17Contract> GetNep17Contracts(IReadOnlyStore store)
{
var scriptHashes = new HashSet<UInt160>();
foreach (var (_, _, notification) in GetNep5Transfers(store))
foreach (var (_, _, notification) in GetNep17Transfers(store))
{
scriptHashes.Add(notification.ScriptHash);
}

foreach (var scriptHash in scriptHashes)
{
if (Nep5Contract.TryLoad(store, scriptHash, out var contract))
if (Nep17Contract.TryLoad(store, scriptHash, out var contract))
{
yield return contract;
}
Expand All @@ -126,11 +126,11 @@ static UInt160 ToUInt160(Neo.VM.Types.StackItem item)
: throw new ArgumentException("invalid UInt160", nameof(item));
}

public static IEnumerable<(Nep5Contract contract, BigInteger balance, uint lastUpdatedBlock)> GetNep5Balances(IReadOnlyStore store, UInt160 address)
public static IEnumerable<(Nep17Contract contract, BigInteger balance, uint lastUpdatedBlock)> GetNep17Balances(IReadOnlyStore store, UInt160 address)
{
var accounts = new Dictionary<UInt160, uint>();

foreach (var (blockIndex, _, notification) in GetNep5Transfers(store))
foreach (var (blockIndex, _, notification) in GetNep17Transfers(store))
{
var from = ToUInt160(notification.State[0]);
var to = ToUInt160(notification.State[1]);
Expand All @@ -144,8 +144,8 @@ static UInt160 ToUInt160(Neo.VM.Types.StackItem item)
{
if (TryGetBalance(kvp.Key, out var balance))
{
var contract = Nep5Contract.TryLoad(store, kvp.Key, out var _contract)
? _contract : Nep5Contract.Unknown(kvp.Key);
var contract = Nep17Contract.TryLoad(store, kvp.Key, out var _contract)
? _contract : Nep17Contract.Unknown(kvp.Key);
yield return (contract, balance, kvp.Value);
}
}
Expand All @@ -168,24 +168,25 @@ bool TryGetBalance(UInt160 asset, out BigInteger balance)
}

[RpcMethod]
public JObject ExpressGetNep5Contracts(JArray _)
public JObject ExpressGetNep17Contracts(JArray _)
{
using var snapshot = Blockchain.Singleton.Store.GetSnapshot();
var jsonContracts = new JArray();
foreach (var contract in GetNep5Contracts(snapshot))
foreach (var contract in GetNep17Contracts(snapshot))
{
var jsonContract = new JObject();
jsonContracts.Add(contract.ToJson());
}
return jsonContracts;
}

[RpcMethod]
public JObject GetNep5Balances(JArray @params)
public JObject GetNep17Balances(JArray @params)
{
var address = GetScriptHashFromParam(@params[0].AsString());
using var snapshot = Blockchain.Singleton.Store.GetSnapshot();
var balances = new JArray();
foreach (var (contract, balance, lastUpdatedBlock) in GetNep5Balances(snapshot, address))
foreach (var (contract, balance, lastUpdatedBlock) in GetNep17Balances(snapshot, address))
{
balances.Add(new JObject()
{
Expand All @@ -202,7 +203,7 @@ public JObject GetNep5Balances(JArray @params)
}

[RpcMethod]
public JObject GetNep5Transfers(JArray @params)
public JObject GetNep17Transfers(JArray @params)
{
var address = GetScriptHashFromParam(@params[0].AsString());

Expand All @@ -218,7 +219,7 @@ public JObject GetNep5Transfers(JArray @params)

{
using var snapshot = Blockchain.Singleton.Store.GetSnapshot();
foreach (var (blockIndex, txIndex, notification) in GetNep5Transfers(snapshot))
foreach (var (blockIndex, txIndex, notification) in GetNep17Transfers(snapshot))
{
var header = Blockchain.Singleton.GetHeader(blockIndex);
if (startTime <= header.Timestamp && header.Timestamp <= endTime)
Expand Down Expand Up @@ -289,7 +290,10 @@ static JObject MakeTransferJson(uint blockIndex, ushort txIndex, NotificationRec
var json = new JArray();
foreach (var (key, value) in contracts)
{
json.Add(value.Manifest.ToJson());
var jsonContract = new JObject();
jsonContract["hash"] = value.Hash.ToString();
jsonContract["manifest"] = value.Manifest.ToJson();
json.Add(jsonContract);
}
return json;
}
Expand Down
6 changes: 3 additions & 3 deletions src/neo3/Node/IExpressNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ internal interface IExpressNode : IDisposable
Task<UInt256> ExecuteAsync(ExpressChain chain, ExpressWalletAccount account, Script script, decimal additionalGas = 0);
Task<UInt256> SubmitTransactionAsync(Transaction tx);
Task<InvokeResult> InvokeAsync(Script script);
Task<(RpcNep5Balance balance, Nep5Contract contract)[]> GetBalancesAsync(UInt160 address);
Task<(RpcNep17Balance balance, Nep17Contract contract)[]> GetBalancesAsync(UInt160 address);
Task<(Transaction tx, RpcApplicationLog? appLog)> GetTransactionAsync(UInt256 txHash);
Task<Block> GetBlockAsync(UInt256 blockHash);
Task<Block> GetBlockAsync(uint blockIndex);
Task<Block> GetLatestBlockAsync();
Task<uint> GetTransactionHeight(UInt256 txHash);
Task<IReadOnlyList<ExpressStorage>> GetStoragesAsync(UInt160 scriptHash);
Task<ContractManifest> GetContractAsync(UInt160 scriptHash);
Task<IReadOnlyList<ContractManifest>> ListContractsAsync();
Task<IReadOnlyList<Nep5Contract>> ListNep5ContractsAsync();
Task<IReadOnlyList<(UInt160 hash, ContractManifest manifest)>> ListContractsAsync();
Task<IReadOnlyList<Nep17Contract>> ListNep17ContractsAsync();
Task<IReadOnlyList<(ulong requestId, OracleRequest request)>> ListOracleRequestsAsync();
Task<UInt256> SubmitOracleResponseAsync(ExpressChain chain, OracleResponse response, ECPoint[] oracleNodes);
}
Expand Down
Loading