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

Implement NCIP-18 #2440

Merged
merged 5 commits into from
Mar 11, 2024
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: 2 additions & 0 deletions .Lib9c.Tests/Action/ActionEvaluationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public ActionEvaluationTest()
[InlineData(typeof(TransferAssets))]
[InlineData(typeof(RuneSummon))]
[InlineData(typeof(ActivateCollection))]
[InlineData(typeof(RetrieveAvatarAssets))]
public void Serialize_With_MessagePack(Type actionType)
{
var action = GetAction(actionType);
Expand Down Expand Up @@ -481,6 +482,7 @@ private ActionBase GetAction(Type type)
),
},
},
RetrieveAvatarAssets _ => new RetrieveAvatarAssets(avatarAddress: new PrivateKey().Address),
_ => throw new InvalidCastException(),
};
}
Expand Down
163 changes: 163 additions & 0 deletions .Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
namespace Lib9c.Tests.Action
{
using System;
using System.Collections.Generic;
using Bencodex.Types;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Libplanet.Types.Assets;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Model.State;
using Nekoyume.Module;
using Nekoyume.TableData;
using Xunit;

public class RetrieveAvatarAssetsTest
{
private static readonly Address _minter = new Address(
new byte[]
{
0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
}
);

#pragma warning disable CS0618
// Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319
private static readonly Currency _currency = Currency.Legacy("NCG", 2, _minter);
#pragma warning restore CS0618

private static readonly Dictionary<string, string>
_csv = TableSheetsImporter.ImportSheets();

private readonly Address _signer;
private readonly IWorld _state;

public RetrieveAvatarAssetsTest()
{
var ca = new CreateAvatar
{
index = 0,
hair = 2,
lens = 3,
ear = 4,
tail = 5,
name = "JohnDoe",
};
_signer = new PrivateKey().Address;
IWorld state = new World(new MockWorldState());
foreach (var (key, value) in _csv)
{
state = state.SetLegacyState(Addresses.GetSheetAddress(key), (Text)value);
}

state = state
.SetLegacyState(
Addresses.GameConfig,
new GameConfigState(_csv[nameof(GameConfigSheet)]).Serialize())
.SetLegacyState(Addresses.GoldCurrency, new GoldCurrencyState(_currency).Serialize());

_state = ca.Execute(new ActionContext
{
PreviousState = state,
BlockIndex = 0,
Signer = _signer,
RandomSeed = 0,
});
}

[Fact]
public void PlainValue()
{
var avatarAddress = new PrivateKey().Address;
var action = new RetrieveAvatarAssets(avatarAddress);
var plainValue = (Dictionary)action.PlainValue;
var values = (Dictionary)plainValue["values"];
Assert.Equal((Text)RetrieveAvatarAssets.TypeIdentifier, plainValue["type_id"]);
Assert.Equal(avatarAddress, values["a"].ToAddress());
}

[Fact]
public void LoadPlainValue()
{
var avatarAddress = new PrivateKey().Address;
var expectedValue = Dictionary.Empty
.Add("type_id", RetrieveAvatarAssets.TypeIdentifier)
.Add("values", Dictionary.Empty.Add("a", avatarAddress.Serialize()));

// Let's assume that serializedAction is the serialized representation of the action, which might be obtained through some other part of your tests
var action = new RetrieveAvatarAssets();
action.LoadPlainValue(expectedValue);

var plainValue = (Dictionary)action.PlainValue;
var values = (Dictionary)plainValue["values"];

Assert.Equal((Text)RetrieveAvatarAssets.TypeIdentifier, plainValue["type_id"]);
Assert.Equal(avatarAddress, values["a"].ToAddress());
}

[Fact]
public void Execute()
{
var agentState = _state.GetAgentState(_signer);
Assert.NotNull(agentState);
var avatarAddress = agentState.avatarAddresses[0];
var prevState = _state.MintAsset(
new ActionContext
{
Signer = _minter,
},
avatarAddress,
1 * _currency
);
Assert.Equal(1 * _currency, prevState.GetBalance(avatarAddress, _currency));

var action = new RetrieveAvatarAssets(avatarAddress);
var nextState = action.Execute(new ActionContext
{
PreviousState = prevState,
BlockIndex = 1L,
Signer = _signer,
RandomSeed = 0,
});
Assert.Equal(0 * _currency, nextState.GetBalance(avatarAddress, _currency));
Assert.Equal(1 * _currency, nextState.GetBalance(_signer, _currency));
}

[Fact]
public void Execute_Throw_FailedLoadStateException()
{
var avatarAddress = new PrivateKey().Address;
var action = new RetrieveAvatarAssets(avatarAddress);

var context = new ActionContext()
{
BlockIndex = 0,
PreviousState = new World(new MockWorldState()),
RandomSeed = 0,
Signer = avatarAddress,
};

Assert.Throws<FailedLoadStateException>(() => action.Execute(context));
}

[Fact]
public void Execute_Throw_ArgumentOutOfRangeException()
{
var agentState = _state.GetAgentState(_signer);
Assert.NotNull(agentState);
var avatarAddress = agentState.avatarAddresses[0];
Assert.Equal(0 * _currency, _state.GetBalance(avatarAddress, _currency));

var action = new RetrieveAvatarAssets(avatarAddress);
Assert.Throws<ArgumentOutOfRangeException>(() => action.Execute(new ActionContext
{
PreviousState = _state,
BlockIndex = 1L,
Signer = _signer,
RandomSeed = 0,
}));
}
}
}
2 changes: 2 additions & 0 deletions Lib9c.MessagePack/Action/NCActionEvaluation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public struct NCActionEvaluation

[Key(8)]
[MessagePackFormatter(typeof(TxIdFormatter))]
#pragma warning disable MsgPack003
public TxId? TxId { get; set; }
#pragma warning restore MsgPack003

[SerializationConstructor]
public NCActionEvaluation(
Expand Down
53 changes: 53 additions & 0 deletions Lib9c/Action/RetrieveAvatarAssets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using Bencodex.Types;
using Libplanet.Action;
using Libplanet.Action.State;
using Libplanet.Crypto;
using Nekoyume.Model.State;
using Nekoyume.Module;

namespace Nekoyume.Action
{
[Serializable]
[ActionType(TypeIdentifier)]
public class RetrieveAvatarAssets: ActionBase
{
public const string TypeIdentifier = "retrieve_avatar_assets";
public Address AvatarAddress;

public RetrieveAvatarAssets()
{
}

public RetrieveAvatarAssets(Address avatarAddress)
{
AvatarAddress = avatarAddress;
}

public override IValue PlainValue => Dictionary.Empty
.Add("type_id", TypeIdentifier)
.Add("values", Dictionary.Empty.Add("a", AvatarAddress.Serialize()));

public override void LoadPlainValue(IValue plainValue)
{
var asDict = (Dictionary)((Dictionary)plainValue)["values"];
AvatarAddress = asDict["a"].ToAddress();
}

public override IWorld Execute(IActionContext context)
{
context.UseGas(1);
Address signer = context.Signer;
var state = context.PreviousState;
var agentState = state.GetAgentState(signer);
if (agentState is not null && agentState.avatarAddresses.ContainsValue(AvatarAddress))
{
var currency = state.GetGoldCurrency();
var balance = state.GetBalance(AvatarAddress, currency);
return state.TransferAsset(context, AvatarAddress, signer, balance);
}

throw new FailedLoadStateException($"signer({signer}) does not contains avatar address({AvatarAddress}).");
}
}
}
Loading