Skip to content

Commit

Permalink
CallEx implementation (neo-project#1364)
Browse files Browse the repository at this point in the history
  • Loading branch information
shargon authored and Tommo-L committed Jun 22, 2020
1 parent 8904114 commit 8c2e743
Show file tree
Hide file tree
Showing 21 changed files with 188 additions and 67 deletions.
7 changes: 7 additions & 0 deletions src/neo/SmartContract/ApplicationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ protected override void LoadContext(ExecutionContext context)
base.LoadContext(context);
}

public ExecutionContext LoadScript(Script script, CallFlags callFlags, int rvcount = -1)
{
ExecutionContext context = LoadScript(script, rvcount);
context.GetState<ExecutionContextState>().CallFlags = callFlags;
return context;
}

public override void Dispose()
{
foreach (IDisposable disposable in disposables)
Expand Down
17 changes: 17 additions & 0 deletions src/neo/SmartContract/CallFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;

namespace Neo.SmartContract
{
[Flags]
public enum CallFlags : byte
{
None = 0,

AllowModifyStates = 0b00000001,
AllowCall = 0b00000010,
AllowNotify = 0b00000100,

ReadOnly = AllowCall | AllowNotify,
All = AllowModifyStates | AllowCall | AllowNotify
}
}
5 changes: 5 additions & 0 deletions src/neo/SmartContract/ExecutionContextState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ internal class ExecutionContextState
/// Calling script hash
/// </summary>
public UInt160 CallingScriptHash { get; set; }

/// <summary>
/// Execution context rights
/// </summary>
public CallFlags CallFlags { get; set; } = CallFlags.All;
}
}
4 changes: 2 additions & 2 deletions src/neo/SmartContract/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap
}
using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas))
{
engine.LoadScript(verification);
engine.LoadScript(verifiable.Witnesses[i].InvocationScript);
engine.LoadScript(verification, CallFlags.ReadOnly);
engine.LoadScript(verifiable.Witnesses[i].InvocationScript, CallFlags.None);
if (engine.Execute() == VMState.FAULT) return false;
if (!engine.ResultStack.TryPop(out StackItem result) || !result.ToBoolean()) return false;
}
Expand Down
12 changes: 7 additions & 5 deletions src/neo/SmartContract/InteropDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,27 @@ public class InteropDescriptor
public long Price { get; }
public Func<EvaluationStack, long> PriceCalculator { get; }
public TriggerType AllowedTriggers { get; }
public CallFlags RequiredCallFlags { get; }

internal InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, long price, TriggerType allowedTriggers)
: this(method, handler, allowedTriggers)
internal InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, long price, TriggerType allowedTriggers, CallFlags requiredCallFlags)
: this(method, handler, allowedTriggers, requiredCallFlags)
{
this.Price = price;
}

internal InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, Func<EvaluationStack, long> priceCalculator, TriggerType allowedTriggers)
: this(method, handler, allowedTriggers)
internal InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, Func<EvaluationStack, long> priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags)
: this(method, handler, allowedTriggers, requiredCallFlags)
{
this.PriceCalculator = priceCalculator;
}

private InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, TriggerType allowedTriggers)
private InteropDescriptor(string method, Func<ApplicationEngine, bool> handler, TriggerType allowedTriggers, CallFlags requiredCallFlags)
{
this.Method = method;
this.Hash = method.ToInteropMethodHash();
this.Handler = handler;
this.AllowedTriggers = allowedTriggers;
this.RequiredCallFlags = requiredCallFlags;
}

public long GetPrice(EvaluationStack stack)
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/InteropService.Binary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ partial class InteropService
{
public static class Binary
{
public static readonly InteropDescriptor Serialize = Register("System.Binary.Serialize", Binary_Serialize, 0_00100000, TriggerType.All);
public static readonly InteropDescriptor Deserialize = Register("System.Binary.Deserialize", Binary_Deserialize, 0_00500000, TriggerType.All);
public static readonly InteropDescriptor Serialize = Register("System.Binary.Serialize", Binary_Serialize, 0_00100000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Deserialize = Register("System.Binary.Deserialize", Binary_Deserialize, 0_00500000, TriggerType.All, CallFlags.None);

private static bool Binary_Serialize(ApplicationEngine engine)
{
Expand Down
12 changes: 6 additions & 6 deletions src/neo/SmartContract/InteropService.Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ partial class InteropService
{
public static class Blockchain
{
public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application);
public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application);
public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application);
public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application);
public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application);
public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application);
public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application, CallFlags.None);
public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application, CallFlags.None);

private static bool Blockchain_GetHeight(ApplicationEngine engine)
{
Expand Down
48 changes: 39 additions & 9 deletions src/neo/SmartContract/InteropService.Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Neo.SmartContract.Manifest;
using Neo.VM;
using Neo.VM.Types;
using System;
using System.Linq;

namespace Neo.SmartContract
Expand All @@ -11,11 +12,12 @@ partial class InteropService
{
public static class Contract
{
public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application);
public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application);
public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application);
public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application);
public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All);
public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates);
public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall);
public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall);
public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None);

private static long GetDeploymentPrice(EvaluationStack stack)
{
Expand Down Expand Up @@ -112,12 +114,34 @@ private static bool Contract_Destroy(ApplicationEngine engine)
private static bool Contract_Call(ApplicationEngine engine)
{
StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop();
StackItem method = engine.CurrentContext.EvaluationStack.Pop();
StackItem args = engine.CurrentContext.EvaluationStack.Pop();

ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan()));
if (contract is null) return false;
return Contract_CallEx(engine, new UInt160(contractHash.GetSpan()), method, args, CallFlags.All);
}

private static bool Contract_CallEx(ApplicationEngine engine)
{
StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop();
StackItem method = engine.CurrentContext.EvaluationStack.Pop();
StackItem args = engine.CurrentContext.EvaluationStack.Pop();

if (!engine.CurrentContext.EvaluationStack.TryPop<PrimitiveType>(out var flagItem))
{
return false;
}

CallFlags flags = (CallFlags)(int)flagItem.ToBigInteger();
if (!Enum.IsDefined(typeof(CallFlags), flags)) return false;

return Contract_CallEx(engine, new UInt160(contractHash.GetSpan()), method, args, flags);
}

private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, StackItem method, StackItem args, CallFlags flags)
{
ContractState contract = engine.Snapshot.Contracts.TryGet(contractHash);
if (contract is null) return false;

ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest;

if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method.GetString()))
Expand All @@ -132,9 +156,15 @@ private static bool Contract_Call(ApplicationEngine engine)
engine.InvocationCounter[contract.ScriptHash] = 1;
}

UInt160 callingScriptHash = engine.CurrentScriptHash;
ExecutionContextState state = engine.CurrentContext.GetState<ExecutionContextState>();
UInt160 callingScriptHash = state.ScriptHash;
CallFlags callingFlags = state.CallFlags;

ExecutionContext context_new = engine.LoadScript(contract.Script, 1);
context_new.GetState<ExecutionContextState>().CallingScriptHash = callingScriptHash;
state = context_new.GetState<ExecutionContextState>();
state.CallingScriptHash = callingScriptHash;
state.CallFlags = flags & callingFlags;

context_new.EvaluationStack.Push(args);
context_new.EvaluationStack.Push(method);
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/InteropService.Crypto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ partial class InteropService
{
public static class Crypto
{
public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All);
public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All);
public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None);

private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack)
{
Expand Down
8 changes: 4 additions & 4 deletions src/neo/SmartContract/InteropService.Enumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ partial class InteropService
{
public static class Enumerator
{
public static readonly InteropDescriptor Create = Register("System.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Next = Register("System.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All);
public static readonly InteropDescriptor Value = Register("System.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Concat = Register("System.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Create = Register("System.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Next = Register("System.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Value = Register("System.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Concat = Register("System.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All, CallFlags.None);

private static bool Enumerator_Create(ApplicationEngine engine)
{
Expand Down
10 changes: 5 additions & 5 deletions src/neo/SmartContract/InteropService.Iterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ partial class InteropService
{
public static class Iterator
{
public static readonly InteropDescriptor Create = Register("System.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Key = Register("System.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Keys = Register("System.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Values = Register("System.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Concat = Register("System.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All);
public static readonly InteropDescriptor Create = Register("System.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Key = Register("System.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Keys = Register("System.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Values = Register("System.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Concat = Register("System.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All, CallFlags.None);

private static bool Iterator_Create(ApplicationEngine engine)
{
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/InteropService.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ partial class InteropService
{
public static class Json
{
public static readonly InteropDescriptor Serialize = Register("System.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All);
public static readonly InteropDescriptor Deserialize = Register("System.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All);
public static readonly InteropDescriptor Serialize = Register("System.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All, CallFlags.None);
public static readonly InteropDescriptor Deserialize = Register("System.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All, CallFlags.None);

private static bool Json_Serialize(ApplicationEngine engine)
{
Expand Down
4 changes: 2 additions & 2 deletions src/neo/SmartContract/InteropService.Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ partial class InteropService
{
internal static class Native
{
public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application);
public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application, CallFlags.AllowModifyStates);

static Native()
{
foreach (NativeContract contract in NativeContract.Contracts)
Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application);
Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application, CallFlags.None);
}

private static bool Native_Deploy(ApplicationEngine engine)
Expand Down
Loading

0 comments on commit 8c2e743

Please sign in to comment.