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

[SC] Support custom InvokeByName method to call methods explicitly rather than using Invoke #542

Open
wants to merge 6 commits into
base: release/1.1.0.0
Choose a base branch
from
Open
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
43 changes: 43 additions & 0 deletions src/Stratis.SmartContracts.CLR/Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using CSharpFunctionalExtensions;
using NBitcoin;
using Stratis.SmartContracts.CLR.Exceptions;
using Stratis.SmartContracts.RuntimeObserver;
Expand All @@ -19,6 +20,11 @@ public class Contract : IContract
/// </summary>
private const BindingFlags DefaultReceiveLookup = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;

/// <summary>
/// The default binding flags for matching the call method. Matches public instance methods declared on the contract type only.
/// </summary>
private const BindingFlags DefaultCallHandlerLookup = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;

private const BindingFlags DefaultBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;

private readonly SmartContract instance;
Expand All @@ -39,6 +45,8 @@ public class Contract : IContract

private MethodInfo receive;

private MethodInfo call;

/// <summary>
/// Returns the receive handler method defined on the inherited contract type. If no receive handler was defined, returns null.
/// </summary>
Expand All @@ -55,6 +63,22 @@ public MethodInfo ReceiveHandler
}
}

/// <summary>
/// Returns the call handler method defined on the inherited contract type. If no receive handler was defined, returns null.
/// </summary>
public MethodInfo CallHandler
{
get
{
if (this.call == null)
{
this.call = this.Type.GetMethod(MethodCall.CallHandlerName, DefaultCallHandlerLookup);
}

return this.call;
}
}

private Contract(SmartContract instance, Type type, ISmartContractState state, uint160 address)
{
this.instance = instance;
Expand Down Expand Up @@ -125,6 +149,10 @@ public IContractInvocationResult Invoke(MethodCall call)
return ContractInvocationResult.Failure(ContractInvocationErrorType.ParameterTypesDontMatch);
}

// Allow the contract to implement custom method resolution.
if (this.CallHandler != null)
return this.InvokeCallHandler(call.Name, invokeParams);

Type[] types = invokeParams.Select(p => p.GetType()).ToArray();

MethodInfo methodToInvoke = this.Type.GetMethod(call.Name, types);
Expand Down Expand Up @@ -157,6 +185,17 @@ private IContractInvocationResult InvokeReceiveHandler()
return this.InvokeInternal(this.ReceiveHandler, null);
}

private IContractInvocationResult InvokeCallHandler(string methodName, object[] parameters)
{
// Handles the scenario where no call handler was defined, but it is attempted to be invoked anyway.
if (this.CallHandler == null)
return ContractInvocationResult.Failure(ContractInvocationErrorType.MethodDoesNotExist);

this.EnsureInitialized();

return this.InvokeInternal(this.CallHandler, new object[] { methodName, parameters });
}

/// <summary>
/// Ensures the contract is initialized by setting its state fields.
/// </summary>
Expand All @@ -179,6 +218,10 @@ private IContractInvocationResult InvokeInternal(MethodBase method, object[] par

return ContractInvocationResult.Success(result);
}
catch (MissingMethodException)
{
return ContractInvocationResult.Failure(ContractInvocationErrorType.MethodDoesNotExist);
}
catch (TargetParameterCountException)
{
// Parameter count incorrect
Expand Down
2 changes: 2 additions & 0 deletions src/Stratis.SmartContracts.CLR/MethodCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class MethodCall
/// </summary>
public const string ReceiveHandlerName = nameof(SmartContract.Receive);

public const string CallHandlerName = "InvokeByName";

/// <summary>
/// Alias for the receive handler method name. Will cause the receive handler to be invoked
/// if specified as the method name in a transaction.
Expand Down