Skip to content

Commit

Permalink
Added GeneratePlainValue() method
Browse files Browse the repository at this point in the history
  • Loading branch information
greymistcube committed Aug 9, 2024
1 parent e4171f9 commit 30d224a
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 18 deletions.
31 changes: 31 additions & 0 deletions Libplanet.SDK.Action.Tests/Sample/Actions/InvalidAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Bencodex.Types;
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;

namespace Libplanet.SDK.Action.Tests.Sample.Actions
{
public class InvalidAction : ActionBase
{
public override Address StorageAddress =>
new Address("0x1000000000000000000000000000000000000000");

[Executable]
public void Add(IValue args)
{
Integer operand = (Integer)args;
Integer stored = GetState(Signer) is IValue value
? (Integer)value
: new Integer(0);
SetState(Signer, new Integer(stored.Value + operand.Value));
}

public void Subtract(IValue args)
{
Integer operand = (Integer)args;
Integer stored = GetState(Signer) is IValue value
? (Integer)value
: new Integer(0);
SetState(Signer, new Integer(stored.Value - operand.Value));
}
}
}
6 changes: 6 additions & 0 deletions Libplanet.SDK.Action.Tests/Sample/Actions/NumberAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,11 @@ public void Multiply(IValue args)
Call<NumberLogAction>(nameof(NumberLogAction.Multiply), new object?[] { operand });
SetState(Signer, new Integer(stored.Value * operand.Value));
}

// Just some random public method for testing.
public void DoNothing()
{
return;
}
}
}
2 changes: 1 addition & 1 deletion Libplanet.SDK.Action.Tests/Sample/Actions/TextAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;

namespace Libplanet.SDK.Action.Tests
namespace Libplanet.SDK.Action.Tests.Sample.Actions
{
[ActionType("Text")]
public class TextAction : ActionBase
Expand Down
46 changes: 40 additions & 6 deletions Libplanet.SDK.Action.Tests/Sample/SampleActionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Libplanet.Action.Loader;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;
using Libplanet.SDK.Action.Tests.Sample.Actions;
using Libplanet.Store;
using Libplanet.Store.Trie;
Expand Down Expand Up @@ -109,23 +110,23 @@ public void TextAppend(bool commit)
[Fact]
public void InvalidPlainValueForLoading()
{
IValue plainValue = Dictionary.Empty // Invalid type_id
IValue plainValue = Dictionary.Empty // Invalid type_id
.Add("type_id", "Run")
.Add("call", "Append")
.Add("args", "Hello");
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));

plainValue = Dictionary.Empty // Missing type_id
plainValue = Dictionary.Empty // Missing type_id
.Add("call", "Append")
.Add("args", "Hello");
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));

plainValue = Dictionary.Empty // Missing call
plainValue = Dictionary.Empty // Missing call
.Add("type_id", "Number")
.Add("args", 5);
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));

plainValue = Dictionary.Empty // Missing args
plainValue = Dictionary.Empty // Missing args
.Add("type_id", "Number")
.Add("call", "Add");
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));
Expand All @@ -134,7 +135,7 @@ public void InvalidPlainValueForLoading()
[Fact]
public void InvalidPlainValueForExecution()
{
IValue plainValue = Dictionary.Empty // Invalid call
IValue plainValue = Dictionary.Empty // Invalid call
.Add("type_id", "Number")
.Add("call", "Divide")
.Add("args", 5);
Expand All @@ -143,7 +144,7 @@ public void InvalidPlainValueForExecution()
Assert.Throws<InvalidOperationException>(() =>
action.Execute(new MockActionContext(address, address, _world)));

plainValue = Dictionary.Empty // Invalid args
plainValue = Dictionary.Empty // Invalid args
.Add("type_id", "Number")
.Add("call", "Add")
.Add("args", "Hello");
Expand All @@ -170,5 +171,38 @@ public void CallableAttributeIsRequired()
action.Execute(new MockActionContext(signer, signer, world)))
.InnerException);
}

[Fact]
public void GeneratePlainValue()
{
IValue expected = Dictionary.Empty
.Add("type_id", "Number")
.Add("call", "Add")
.Add("args", 5);
IValue generated = ActionBase.GeneratePlainValue<NumberAction>(
"Add", new Integer(5));
Assert.Equal(expected, generated);

expected = Dictionary.Empty
.Add("type_id", "Text")
.Add("call", "Append")
.Add("args", "Hello");
generated = ActionBase.GeneratePlainValue<TextAction>(
"Append", new Text("Hello"));
Assert.Equal(expected, generated);

Assert.Contains(
$"{nameof(ActionTypeAttribute)}",
Assert.Throws<ArgumentException>(() =>
ActionBase.GeneratePlainValue<InvalidAction>("Add", new Integer(5))).Message);
Assert.Contains(
$"cannot be found",
Assert.Throws<ArgumentException>(() =>
ActionBase.GeneratePlainValue<NumberAction>("Divide", new Integer(5))).Message);
Assert.Contains(
$"{nameof(ExecutableAttribute)}",
Assert.Throws<ArgumentException>(() =>
ActionBase.GeneratePlainValue<NumberAction>("DoNothing", new Integer(5))).Message);
}
}
}
56 changes: 45 additions & 11 deletions Libplanet.SDK.Action/Action/ActionBase.API.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
using System.Reflection;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Crypto;
using Libplanet.SDK.Action.Attributes;

namespace Libplanet.SDK.Action
{
public partial class ActionBase
{
public static IValue GeneratePlainValue<T>(string methodName, IValue args)
where T : ActionBase
{
ActionTypeAttribute actionType = typeof(T).GetCustomAttribute<ActionTypeAttribute>() ??
throw new ArgumentException(
$"Type is missing a {nameof(ActionTypeAttribute)}.");

MethodInfo methodInfo = typeof(T).GetMethod(methodName) ??
throw new ArgumentException(
$"Method named {methodName} cannot be found for {typeof(T)}.",
nameof(methodName));
if (methodInfo.GetCustomAttribute<ExecutableAttribute>() is null)
{
throw new ArgumentException(
$"Target method is missing a {nameof(ExecutableAttribute)}.",
nameof(methodName));
}

return Dictionary.Empty
.Add("type_id", actionType.TypeIdentifier)
.Add("call", methodName)
.Add("args", args);
}

protected IValue? GetState(Address address)
=> World.GetAccount(StorageAddress).GetState(address);

Expand All @@ -28,11 +53,12 @@ protected void Call<T>(string methodName, object?[]? args = null)
calledAction.LoadContext(ActionContext, World);

MethodInfo methodInfo = typeof(T).GetMethod(methodName) ??
throw new Exception("Method cannot be found.");
throw new ArgumentException(
$"Method named {methodName} cannot be found.");
if (methodInfo.GetCustomAttribute<CallableAttribute>() is null)
{
throw new Exception(
$"Target method is missing a {nameof(CallableAttribute)}");
throw new ArgumentException(
$"Target method {methodName} is missing a {nameof(CallableAttribute)}");
}

methodInfo.Invoke(calledAction, args);
Expand All @@ -41,28 +67,36 @@ protected void Call<T>(string methodName, object?[]? args = null)
_actionContext = calledAction._actionContext;
}

protected U Call<T, U>(string methodName, object?[]? args = null)
where T : ActionBase
protected TR Call<TA, TR>(string methodName, object?[]? args = null)
where TA : ActionBase
{
if (Activator.CreateInstance(typeof(T)) is not T calledAction)
if (Activator.CreateInstance(typeof(TA)) is not TA calledAction)
{
throw new Exception("Action cannot be found.");
}

calledAction.LoadContext(ActionContext, World);

MethodInfo methodInfo = typeof(T).GetMethod(methodName) ??
throw new Exception("Method cannot be found.");
MethodInfo methodInfo = typeof(TA).GetMethod(methodName) ??
throw new ArgumentException(
$"Method named {methodName} cannot be found.");
if (methodInfo.GetCustomAttribute<CallableAttribute>() is null)
{
throw new ArgumentException(
$"Target method {methodName} is missing a {nameof(CallableAttribute)}");
}

if (methodInfo.Invoke(calledAction, args) is not U result)
var result = methodInfo.Invoke(calledAction, args);
if (result is not TR typedResult)
{
throw new Exception("Return type doesn't match.");
throw new Exception(
$"Return type is expected to be {typeof(TR)}: {result?.GetType()}");
}

_world = calledAction._world;
_actionContext = calledAction._actionContext;

return result;
return typedResult;
}
}
}

0 comments on commit 30d224a

Please sign in to comment.