From 17fea4cb37429e0dd7247e1cea4ddf8565883bec Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 5 Mar 2024 18:33:29 +0900 Subject: [PATCH 1/5] Disable MsgPack003 --- Lib9c.MessagePack/Action/NCActionEvaluation.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib9c.MessagePack/Action/NCActionEvaluation.cs b/Lib9c.MessagePack/Action/NCActionEvaluation.cs index 35f2b94a68..f7df7c63bd 100644 --- a/Lib9c.MessagePack/Action/NCActionEvaluation.cs +++ b/Lib9c.MessagePack/Action/NCActionEvaluation.cs @@ -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( From 4869bdc8dc6359dcb3cf52878c223d180c00c7d8 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Wed, 6 Mar 2024 17:17:29 +0900 Subject: [PATCH 2/5] Introduce RetrieveAvatarAssets --- .Lib9c.Tests/Action/ActionEvaluationTest.cs | 2 + .../Action/RetrieveAvatarAssetsTest.cs | 137 ++++++++++++++++++ Lib9c/Action/RetrieveAvatarAssets.cs | 55 +++++++ 3 files changed, 194 insertions(+) create mode 100644 .Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs create mode 100644 Lib9c/Action/RetrieveAvatarAssets.cs diff --git a/.Lib9c.Tests/Action/ActionEvaluationTest.cs b/.Lib9c.Tests/Action/ActionEvaluationTest.cs index 3a58483705..d6ee4a2392 100644 --- a/.Lib9c.Tests/Action/ActionEvaluationTest.cs +++ b/.Lib9c.Tests/Action/ActionEvaluationTest.cs @@ -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); @@ -481,6 +482,7 @@ private ActionBase GetAction(Type type) ), }, }, + RetrieveAvatarAssets _ => new RetrieveAvatarAssets(avatarAddress: new PrivateKey().Address), _ => throw new InvalidCastException(), }; } diff --git a/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs b/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs new file mode 100644 index 0000000000..650b217cf2 --- /dev/null +++ b/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs @@ -0,0 +1,137 @@ +namespace Lib9c.Tests.Action +{ + 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 + _csv = TableSheetsImporter.ImportSheets(); + + [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 ca = new CreateAvatar + { + index = 0, + hair = 2, + lens = 3, + ear = 4, + tail = 5, + name = "JohnDoe", + }; + var 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()); + + var prevState = ca.Execute(new ActionContext + { + PreviousState = state, + BlockIndex = 0, + Signer = signer, + RandomSeed = 0, + }); + var agentState = prevState.GetAgentState(signer); + Assert.NotNull(agentState); + var avatarAddress = agentState.avatarAddresses[0]; + prevState = prevState.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(() => action.Execute(context)); + } + } +} diff --git a/Lib9c/Action/RetrieveAvatarAssets.cs b/Lib9c/Action/RetrieveAvatarAssets.cs new file mode 100644 index 0000000000..24efff5bd9 --- /dev/null +++ b/Lib9c/Action/RetrieveAvatarAssets.cs @@ -0,0 +1,55 @@ +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; + if (state.TryGetAvatarState(signer, AvatarAddress, out _)) + { + var currency = state.GetGoldCurrency(); + var balance = state.GetBalance(AvatarAddress, currency); + if (balance > 0 * currency) + { + return state.TransferAsset(context, AvatarAddress, signer, balance); + } + } + + throw new FailedLoadStateException($"signer({signer}) does not contains avatar address({AvatarAddress})."); + } + } +} From d4a4156906b61009cedbf28a4e33f7672cc9b5e1 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 8 Mar 2024 16:57:18 +0900 Subject: [PATCH 3/5] Delete balance check --- .../Action/RetrieveAvatarAssetsTest.cs | 91 ++++++++++++------- Lib9c/Action/RetrieveAvatarAssets.cs | 5 +- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs b/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs index 650b217cf2..3eea4a53d5 100644 --- a/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs +++ b/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs @@ -30,6 +30,42 @@ public class RetrieveAvatarAssetsTest private static readonly Dictionary _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() { @@ -63,39 +99,10 @@ public void LoadPlainValue() [Fact] public void Execute() { - var ca = new CreateAvatar - { - index = 0, - hair = 2, - lens = 3, - ear = 4, - tail = 5, - name = "JohnDoe", - }; - var 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()); - - var prevState = ca.Execute(new ActionContext - { - PreviousState = state, - BlockIndex = 0, - Signer = signer, - RandomSeed = 0, - }); - var agentState = prevState.GetAgentState(signer); + var agentState = _state.GetAgentState(_signer); Assert.NotNull(agentState); var avatarAddress = agentState.avatarAddresses[0]; - prevState = prevState.MintAsset( + var prevState = _state.MintAsset( new ActionContext { Signer = _minter, @@ -110,11 +117,11 @@ public void Execute() { PreviousState = prevState, BlockIndex = 1L, - Signer = signer, + Signer = _signer, RandomSeed = 0, }); Assert.Equal(0 * _currency, nextState.GetBalance(avatarAddress, _currency)); - Assert.Equal(1 * _currency, nextState.GetBalance(signer, _currency)); + Assert.Equal(1 * _currency, nextState.GetBalance(_signer, _currency)); } [Fact] @@ -133,5 +140,23 @@ public void Execute_Throw_FailedLoadStateException() Assert.Throws(() => action.Execute(context)); } + + [Fact] + public void Execute_Throw_InsufficientBalanceException() + { + 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(() => action.Execute(new ActionContext + { + PreviousState = _state, + BlockIndex = 1L, + Signer = _signer, + RandomSeed = 0, + })); + } } } diff --git a/Lib9c/Action/RetrieveAvatarAssets.cs b/Lib9c/Action/RetrieveAvatarAssets.cs index 24efff5bd9..225a566a67 100644 --- a/Lib9c/Action/RetrieveAvatarAssets.cs +++ b/Lib9c/Action/RetrieveAvatarAssets.cs @@ -43,10 +43,7 @@ public override IWorld Execute(IActionContext context) { var currency = state.GetGoldCurrency(); var balance = state.GetBalance(AvatarAddress, currency); - if (balance > 0 * currency) - { - return state.TransferAsset(context, AvatarAddress, signer, balance); - } + return state.TransferAsset(context, AvatarAddress, signer, balance); } throw new FailedLoadStateException($"signer({signer}) does not contains avatar address({AvatarAddress})."); From f089d44c92a1367c7933749b768d0e8a2636067d Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 8 Mar 2024 17:21:01 +0900 Subject: [PATCH 4/5] Change exception type --- .Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs b/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs index 3eea4a53d5..c0176c9d3f 100644 --- a/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs +++ b/.Lib9c.Tests/Action/RetrieveAvatarAssetsTest.cs @@ -1,5 +1,6 @@ namespace Lib9c.Tests.Action { + using System; using System.Collections.Generic; using Bencodex.Types; using Libplanet.Action.State; @@ -142,7 +143,7 @@ public void Execute_Throw_FailedLoadStateException() } [Fact] - public void Execute_Throw_InsufficientBalanceException() + public void Execute_Throw_ArgumentOutOfRangeException() { var agentState = _state.GetAgentState(_signer); Assert.NotNull(agentState); @@ -150,7 +151,7 @@ public void Execute_Throw_InsufficientBalanceException() Assert.Equal(0 * _currency, _state.GetBalance(avatarAddress, _currency)); var action = new RetrieveAvatarAssets(avatarAddress); - Assert.Throws(() => action.Execute(new ActionContext + Assert.Throws(() => action.Execute(new ActionContext { PreviousState = _state, BlockIndex = 1L, From 6d0b53b9de3838ee307b83f98d9b678dd40df9b6 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 8 Mar 2024 17:59:18 +0900 Subject: [PATCH 5/5] Check AgentState.avatarAddresses instead of GetAvatarState --- Lib9c/Action/RetrieveAvatarAssets.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib9c/Action/RetrieveAvatarAssets.cs b/Lib9c/Action/RetrieveAvatarAssets.cs index 225a566a67..719a3068da 100644 --- a/Lib9c/Action/RetrieveAvatarAssets.cs +++ b/Lib9c/Action/RetrieveAvatarAssets.cs @@ -39,7 +39,8 @@ public override IWorld Execute(IActionContext context) context.UseGas(1); Address signer = context.Signer; var state = context.PreviousState; - if (state.TryGetAvatarState(signer, AvatarAddress, out _)) + var agentState = state.GetAgentState(signer); + if (agentState is not null && agentState.avatarAddresses.ContainsValue(AvatarAddress)) { var currency = state.GetGoldCurrency(); var balance = state.GetBalance(AvatarAddress, currency);