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

Optimize dynamic call #2211

Merged
merged 5 commits into from
Jan 10, 2021
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
2 changes: 1 addition & 1 deletion src/neo/Network/P2P/Payloads/OracleResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class OracleResponse : TransactionAttribute
static OracleResponse()
{
using ScriptBuilder sb = new ScriptBuilder();
sb.EmitDynamicCall(NativeContract.Oracle.Hash, "finish", false);
sb.EmitDynamicCall(NativeContract.Oracle.Hash, "finish");
FixedScript = sb.ToArray();
}

Expand Down
19 changes: 12 additions & 7 deletions src/neo/SmartContract/ApplicationEngine.Contract.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Neo.Cryptography.ECC;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract.Manifest;
using Neo.SmartContract.Native;
using Neo.VM.Types;
using System;
using Array = Neo.VM.Types.Array;

namespace Neo.SmartContract
{
Expand All @@ -20,17 +22,20 @@ partial class ApplicationEngine
public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates);
public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates);

protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, bool hasReturnValue, ushort pcount)
protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, Array args)
{
if (method.StartsWith('_')) throw new ArgumentException($"Invalid Method Name: {method}");
if ((callFlags & ~CallFlags.All) != 0)
throw new ArgumentOutOfRangeException(nameof(callFlags));
if (pcount > CurrentContext.EvaluationStack.Count)
throw new InvalidOperationException();
StackItem[] args = new StackItem[pcount];
for (int i = 0; i < pcount; i++)
args[i] = Pop();
CallContractInternal(contractHash, method, callFlags, hasReturnValue, args);

ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash);
if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}");
ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method);
if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}");
bool hasReturnValue = md.ReturnType != ContractParameterType.Void;

if (!hasReturnValue) CurrentContext.EvaluationStack.Push(StackItem.Null);
shargon marked this conversation as resolved.
Show resolved Hide resolved
CallContractInternal(contract, md, callFlags, hasReturnValue, args);
}

protected internal void CallNativeContract(string name)
Expand Down
20 changes: 12 additions & 8 deletions src/neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,20 @@ private ExecutionContext CallContractInternal(UInt160 contractHash, string metho
if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}");
ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method);
if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}");
return CallContractInternal(contract, md, flags, hasReturnValue, args);
}

if (md.Safe)
private ExecutionContext CallContractInternal(ContractState contract, ContractMethodDescriptor method, CallFlags flags, bool hasReturnValue, IReadOnlyList<StackItem> args)
{
if (method.Safe)
{
flags &= ~CallFlags.WriteStates;
}
else
{
ContractState currentContract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash);
if (currentContract?.CanCall(contract, method) == false)
throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contractHash} From Contract {CurrentScriptHash}");
if (currentContract?.CanCall(contract, method.Name) == false)
throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contract.Hash} From Contract {CurrentScriptHash}");
}

if (invocationCounter.TryGetValue(contract.Hash, out var counter))
Expand All @@ -107,16 +111,16 @@ private ExecutionContext CallContractInternal(UInt160 contractHash, string metho
UInt160 callingScriptHash = state.ScriptHash;
CallFlags callingFlags = state.CallFlags;

if (args.Length != md.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {md.Parameters.Length} Arguments But Receives {args.Length} Arguments");
if (hasReturnValue ^ (md.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match.");
ExecutionContext context_new = LoadContract(contract, method, flags & callingFlags, hasReturnValue, (ushort)args.Length);
if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments");
if (hasReturnValue ^ (method.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match.");
ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue, (ushort)args.Count);
state = context_new.GetState<ExecutionContextState>();
state.CallingScriptHash = callingScriptHash;

for (int i = args.Length - 1; i >= 0; i--)
for (int i = args.Count - 1; i >= 0; i--)
context_new.EvaluationStack.Push(args[i]);
if (NativeContract.IsNative(contract.Hash))
context_new.EvaluationStack.Push(method);
context_new.EvaluationStack.Push(method.Name);

return context_new;
}
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ internal virtual void PostPersist(ApplicationEngine engine)
{
}

public ApplicationEngine TestCall(string operation, bool hasReturnValue, params object[] args)
public ApplicationEngine TestCall(string operation, params object[] args)
{
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitDynamicCall(Hash, operation, hasReturnValue, args);
sb.EmitDynamicCall(Hash, operation, args);
return ApplicationEngine.Run(sb.ToArray());
}
}
Expand Down
17 changes: 8 additions & 9 deletions src/neo/VM/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,36 +46,35 @@ public static ScriptBuilder Emit(this ScriptBuilder sb, params OpCode[] ops)
return sb;
}

public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue)
public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation)
{
sb.EmitPush(0);
sb.EmitPush(hasReturnValue ? 1 : 0);
sb.Emit(OpCode.NEWARRAY0);
sb.EmitPush(CallFlags.All);
sb.EmitPush(operation);
sb.EmitPush(scriptHash);
sb.EmitSysCall(ApplicationEngine.System_Contract_Call);
return sb;
}

public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue, params ContractParameter[] args)
public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params ContractParameter[] args)
{
for (int i = args.Length - 1; i >= 0; i--)
sb.EmitPush(args[i]);
sb.EmitPush(args.Length);
sb.EmitPush(hasReturnValue ? 1 : 0);
sb.Emit(OpCode.PACK);
sb.EmitPush(CallFlags.All);
sb.EmitPush(operation);
sb.EmitPush(scriptHash);
sb.EmitSysCall(ApplicationEngine.System_Contract_Call);
return sb;
}

public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue, params object[] args)
public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params object[] args)
{
for (int i = args.Length - 1; i >= 0; i--)
sb.EmitPush(args[i]);
sb.EmitPush(args.Length);
sb.EmitPush(hasReturnValue ? 1 : 0);
sb.Emit(OpCode.PACK);
sb.EmitPush(CallFlags.All);
sb.EmitPush(operation);
sb.EmitPush(scriptHash);
Expand Down Expand Up @@ -213,10 +212,10 @@ public static ScriptBuilder EmitSysCall(this ScriptBuilder sb, uint method, para
/// <param name="operation">contract operation</param>
/// <param name="args">operation arguments</param>
/// <returns></returns>
public static byte[] MakeScript(this UInt160 scriptHash, string operation, bool hasReturnValue, params object[] args)
public static byte[] MakeScript(this UInt160 scriptHash, string operation, params object[] args)
{
using ScriptBuilder sb = new ScriptBuilder();
sb.EmitDynamicCall(scriptHash, operation, hasReturnValue, args);
sb.EmitDynamicCall(scriptHash, operation, args);
return sb.ToArray();
}

Expand Down
4 changes: 2 additions & 2 deletions src/neo/Wallets/AssetDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public AssetDescriptor(UInt160 asset_id)
byte[] script;
using (ScriptBuilder sb = new ScriptBuilder())
{
sb.EmitDynamicCall(asset_id, "decimals", true);
sb.EmitDynamicCall(asset_id, "symbol", true);
sb.EmitDynamicCall(asset_id, "decimals");
sb.EmitDynamicCall(asset_id, "symbol");
script = sb.ToArray();
}
using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_10000000);
Expand Down
8 changes: 4 additions & 4 deletions src/neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts)
sb.EmitPush(0);
foreach (UInt160 account in accounts)
{
sb.EmitDynamicCall(asset_id, "balanceOf", true, account);
sb.EmitDynamicCall(asset_id, "balanceOf", account);
sb.Emit(OpCode.ADD);
}
sb.EmitDynamicCall(asset_id, "decimals", true);
sb.EmitDynamicCall(asset_id, "decimals");
script = sb.ToArray();
}
using ApplicationEngine engine = ApplicationEngine.Run(script, gas: 20000000L * accounts.Length);
Expand Down Expand Up @@ -265,7 +265,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null
foreach (UInt160 account in accounts)
using (ScriptBuilder sb2 = new ScriptBuilder())
{
sb2.EmitDynamicCall(assetId, "balanceOf", true, account);
sb2.EmitDynamicCall(assetId, "balanceOf", account);
using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot))
{
if (engine.State.HasFlag(VMState.FAULT))
Expand Down Expand Up @@ -296,7 +296,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null
Scopes = WitnessScope.CalledByEntry
});
}
sb.EmitDynamicCall(output.AssetId, "transfer", true, account, output.ScriptHash, value, output.Data);
sb.EmitDynamicCall(output.AssetId, "transfer", account, output.ScriptHash, value, output.Data);
sb.Emit(OpCode.ASSERT);
}
}
Expand Down
6 changes: 3 additions & 3 deletions tests/neo.UnitTests/Extensions/NativeContractExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static class NativeContractExtensions
public static ContractState DeployContract(this StoreView snapshot, UInt160 sender, byte[] nefFile, byte[] manifest, long gas = 200_00000000)
{
var script = new ScriptBuilder();
script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", true, nefFile, manifest);
script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest);

var engine = ApplicationEngine.Create(TriggerType.Application,
sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, null, gas);
Expand All @@ -34,7 +34,7 @@ public static ContractState DeployContract(this StoreView snapshot, UInt160 send
public static void UpdateContract(this StoreView snapshot, UInt160 callingScriptHash, byte[] nefFile, byte[] manifest)
{
var script = new ScriptBuilder();
script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", false, nefFile, manifest);
script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest);

var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot);
engine.LoadScript(script.ToArray());
Expand All @@ -57,7 +57,7 @@ public static void UpdateContract(this StoreView snapshot, UInt160 callingScript
public static void DestroyContract(this StoreView snapshot, UInt160 callingScriptHash)
{
var script = new ScriptBuilder();
script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy", false);
script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy");

var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot);
engine.LoadScript(script.ToArray());
Expand Down
14 changes: 7 additions & 7 deletions tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ public void FeeIsSignatureContract_TestScope_Global()
{
// self-transfer of 1e-8 GAS
BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down Expand Up @@ -405,7 +405,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS()
{
// self-transfer of 1e-8 GAS
BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down Expand Up @@ -492,7 +492,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS()
{
// self-transfer of 1e-8 GAS
System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down Expand Up @@ -580,7 +580,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT()
{
// self-transfer of 1e-8 GAS
System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down Expand Up @@ -632,7 +632,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS()
{
// self-transfer of 1e-8 GAS
BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down Expand Up @@ -722,7 +722,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT()
{
// self-transfer of 1e-8 GAS
BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down Expand Up @@ -985,7 +985,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default()
{
// self-transfer of 1e-8 GAS
BigInteger value = (new BigDecimal(1, 8)).Value;
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null);
sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null);
sb.Emit(OpCode.ASSERT);
script = sb.ToArray();
}
Expand Down
Loading