From 642c1ce902c56b2cd85b4018b6031ec673f8ffb9 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Tue, 29 Aug 2023 19:17:38 +0900 Subject: [PATCH 01/35] bump lib9c:development --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index cda895013..45c6a94ab 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit cda8950136a3021539116a87a4d567bfe06f0604 +Subproject commit 45c6a94ab6c162d5771fa5dabaef160af2307b21 From 86bf7ccc3a56cb0537b74fe6de1f2d2a981c2410 Mon Sep 17 00:00:00 2001 From: XxshiftxX Date: Wed, 30 Aug 2023 09:22:08 +0900 Subject: [PATCH 02/35] make patchTableSheet can recieve tableName with new form --- NineChronicles.Headless/GraphTypes/ActionQuery.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NineChronicles.Headless/GraphTypes/ActionQuery.cs b/NineChronicles.Headless/GraphTypes/ActionQuery.cs index 070458f12..207be91b5 100644 --- a/NineChronicles.Headless/GraphTypes/ActionQuery.cs +++ b/NineChronicles.Headless/GraphTypes/ActionQuery.cs @@ -223,7 +223,7 @@ public ActionQuery(StandaloneContext standaloneContext) @namespace.StartsWith($"{nameof(Nekoyume)}.{nameof(Nekoyume.TableData)}") && !type.IsAbstract && typeof(ISheet).IsAssignableFrom(type) && - type.Name == tableName); + tableName.Split('_').First() == type.Name); } catch (Exception) { From bd2c76e581b95f791c4cf4d50c21e6b875f9fd5c Mon Sep 17 00:00:00 2001 From: moreal Date: Tue, 29 Aug 2023 09:57:31 +0900 Subject: [PATCH 03/35] Bump Lib9c to feat/revise-staking --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 45c6a94ab..4557d10a5 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 45c6a94ab6c162d5771fa5dabaef160af2307b21 +Subproject commit 4557d10a576793d63563ff77d6e28a13135a00e9 From 3cf4ea4cc9d8b73bcbf56644fffd83ff2ab9332e Mon Sep 17 00:00:00 2001 From: moreal Date: Wed, 30 Aug 2023 11:28:44 +0900 Subject: [PATCH 04/35] Support `StakeStateV2` in GraphQL --- .../States/Models/StakeStateTypeTest.cs | 6 +- .../GraphTypes/StateQuery.cs | 20 +++- .../GraphTypes/States/StakeStateType.cs | 97 +++++++++++++++++-- 3 files changed, 109 insertions(+), 14 deletions(-) diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs index a08ff0969..ad96debf9 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs @@ -36,7 +36,11 @@ public async Task Query(StakeState stakeState, long deposit, long blockIndex, Di claimableBlockIndex }"; var queryResult = await ExecuteQueryAsync( - query, source: new StakeStateType.StakeStateContext(stakeState, mockState, blockIndex)); + query, + source: new StakeStateType.StakeStateContext( + new StakeStateType.StakeStateContext.StakeStateWrapper(stakeState), + mockState, + blockIndex)); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; Assert.Equal(expected, data); } diff --git a/NineChronicles.Headless/GraphTypes/StateQuery.cs b/NineChronicles.Headless/GraphTypes/StateQuery.cs index ed61ab8ba..b41390109 100644 --- a/NineChronicles.Headless/GraphTypes/StateQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StateQuery.cs @@ -13,6 +13,7 @@ using Nekoyume.Extensions; using Nekoyume.Model.Arena; using Nekoyume.Model.Item; +using Nekoyume.Model.Stake; using Nekoyume.Model.State; using Nekoyume.TableData; using Nekoyume.TableData.Crystal; @@ -239,10 +240,23 @@ public StateQuery() StakeStateType.StakeStateContext? GetStakeState(StateContext ctx, Address agentAddress) { - if (ctx.GetState(StakeState.DeriveAddress(agentAddress)) is Dictionary state) + var stakeStateAddress = StakeState.DeriveAddress(agentAddress); + var stakeStateValue = ctx.GetState(stakeStateAddress); + if (stakeStateValue is Dictionary dictionary) { return new StakeStateType.StakeStateContext( - new StakeState(state), + new StakeStateType.StakeStateContext.StakeStateWrapper(new StakeState(dictionary)), + ctx.AccountState, + ctx.BlockIndex + ); + } + + if (stakeStateValue is List list) + { + return new StakeStateType.StakeStateContext( + new StakeStateType.StakeStateContext.StakeStateWrapper( + new StakeStateV2(list), + stakeStateAddress), ctx.AccountState, ctx.BlockIndex ); @@ -252,7 +266,7 @@ public StateQuery() } Field( - name: nameof(StakeState), + name: "stakeState", description: "State for staking.", arguments: new QueryArguments(new QueryArgument> { diff --git a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs index d7a4ee811..b4b5f4a2f 100644 --- a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs +++ b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using Bencodex.Types; +using GraphQL; using GraphQL.Types; using Libplanet.Action; using Libplanet.Explorer.GraphTypes; using Libplanet.Action.State; +using Libplanet.Crypto; using Nekoyume.Model.State; using NineChronicles.Headless.GraphTypes.States.Models; using NineChronicles.Headless.GraphTypes.States.Models.World; @@ -12,6 +15,9 @@ using NineChronicles.Headless.GraphTypes.States.Models.Quest; using Nekoyume.Blockchain.Policy; using Nekoyume; +using Nekoyume.Model.Stake; +using Nekoyume.TableData; +using NineChronicles.Headless.GraphTypes.Abstractions; namespace NineChronicles.Headless.GraphTypes.States { @@ -19,38 +25,81 @@ public class StakeStateType : ObjectGraphType { public class StakeStateContext : StateContext { - public StakeStateContext(StakeState stakeState, IAccountState accountState, long blockIndex) + public class StakeStateWrapper + { + private readonly StakeState? _v1; + private readonly StakeStateV2? _v2; + private readonly Address? _v2Address; + + public StakeStateWrapper(StakeState stakeState) + { + _v1 = stakeState; + _v2 = null; + _v2Address = null; + } + + public StakeStateWrapper(StakeStateV2 stakeStateV2, Address stakeStateV2Address) + { + _v1 = null; + _v2 = stakeStateV2; + _v2Address = stakeStateV2Address; + } + + public long StartedBlockIndex => _v1?.StartedBlockIndex ?? + _v2?.StartedBlockIndex ?? throw new InvalidOperationException(); + + public long ReceivedBlockIndex => _v1?.ReceivedBlockIndex ?? + _v2?.ReceivedBlockIndex ?? throw new InvalidOperationException(); + + public long GetClaimableBlockIndex(long blockIndex) => _v1?.GetClaimableBlockIndex(blockIndex) ?? + _v2?.ClaimableBlockIndex ?? + throw new InvalidOperationException(); + + public long CancellableBlockIndex => _v1?.CancellableBlockIndex ?? + _v2?.CancellableBlockIndex ?? + throw new InvalidOperationException(); + + public StakeState.StakeAchievements? Achievements => _v1?.Achievements; + + public Address Address => _v1?.address ?? + _v2Address ?? + throw new InvalidOperationException(); + + public Contract? Contract => _v2?.Contract; + } + + public StakeStateContext(StakeStateWrapper stakeStateWrapper, IAccountState accountState, long blockIndex) : base(accountState, blockIndex) { - StakeState = stakeState; + StakeState = stakeStateWrapper; } - public StakeState StakeState { get; } + public StakeStateWrapper StakeState { get; } } public StakeStateType() { Field>( - nameof(StakeState.address), + "address", description: "The address of current state.", - resolve: context => context.Source.StakeState.address); + resolve: context => context.Source.StakeState.Address); Field>( "deposit", description: "The staked amount.", resolve: context => context.Source.AccountState.GetBalance( - context.Source.StakeState.address, + context.Source.StakeState.Address, new GoldCurrencyState((Dictionary)context.Source.GetState(GoldCurrencyState.Address)!).Currency) .GetQuantityString(true)); Field>( - nameof(StakeState.StartedBlockIndex), + "startedBlockIndex", description: "The block index the user started to stake.", resolve: context => context.Source.StakeState.StartedBlockIndex); Field>( - nameof(StakeState.ReceivedBlockIndex), + "receivedBlockIndex", description: "The block index the user received rewards.", resolve: context => context.Source.StakeState.ReceivedBlockIndex); Field>( - nameof(StakeState.CancellableBlockIndex), + "cancellableBlockIndex", description: "The block index the user can cancel the staking.", resolve: context => context.Source.StakeState.CancellableBlockIndex); Field>( @@ -58,10 +107,38 @@ public StakeStateType() description: "The block index the user can claim rewards.", resolve: context => context.Source.StakeState.GetClaimableBlockIndex( context.Source.BlockIndex)); - Field>( + Field( nameof(StakeState.Achievements), description: "The staking achievements.", resolve: context => context.Source.StakeState.Achievements); + Field( + "stakeRewards", + resolve: context => + { + if (context.Source.StakeState.Contract is not { } contract) + { + return null; + } + + IReadOnlyList values = context.Source.GetStates(new[] + { + Addresses.GetSheetAddress(contract.StakeRegularFixedRewardSheetTableName), + Addresses.GetSheetAddress(contract.StakeRegularRewardSheetTableName), + }); + + if (!(values[0] is Text fsv && values[1] is Text sv)) + { + throw new ExecutionError("Could not found stake rewards sheets"); + } + + StakeRegularFixedRewardSheet stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet(); + StakeRegularRewardSheet stakeRegularRewardSheet = new StakeRegularRewardSheet(); + stakeRegularFixedRewardSheet.Set(fsv); + stakeRegularRewardSheet.Set(sv); + + return (stakeRegularRewardSheet, stakeRegularFixedRewardSheet); + } + ); } } } From f8a750853a6acae4d7b5bc5c85c0ee48ca8065ab Mon Sep 17 00:00:00 2001 From: moreal Date: Wed, 30 Aug 2023 11:32:38 +0900 Subject: [PATCH 05/35] Provide a new `latestStakeRewards` field --- .../GraphTypes/StateQuery.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/NineChronicles.Headless/GraphTypes/StateQuery.cs b/NineChronicles.Headless/GraphTypes/StateQuery.cs index b41390109..9f1ca1c69 100644 --- a/NineChronicles.Headless/GraphTypes/StateQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StateQuery.cs @@ -17,6 +17,7 @@ using Nekoyume.Model.State; using Nekoyume.TableData; using Nekoyume.TableData.Crystal; +using Nekoyume.TableData.Stake; using NineChronicles.Headless.GraphTypes.Abstractions; using NineChronicles.Headless.GraphTypes.States; using NineChronicles.Headless.GraphTypes.States.Models; @@ -352,8 +353,42 @@ public StateQuery() } ); + Field( + "latestStakeRewards", + description: "The latest stake rewards based on StakePolicySheet.", + resolve: context => + { + var stakePolicySheetStateValue = context.Source.GetState(Addresses.GetSheetAddress()); + var stakePolicySheet = new StakePolicySheet(); + if (stakePolicySheetStateValue is not Text stakePolicySheetStateText) + { + return null; + } + + stakePolicySheet.Set(stakePolicySheetStateText); + + IReadOnlyList values = context.Source.GetStates(new[] + { + Addresses.GetSheetAddress(stakePolicySheet["StakeRegularFixedRewardSheet"].Value), + Addresses.GetSheetAddress(stakePolicySheet["StakeRegularRewardSheet"].Value), + }); + + if (!(values[0] is Text fsv && values[1] is Text sv)) + { + return null; + } + + var stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet(); + var stakeRegularRewardSheet = new StakeRegularRewardSheet(); + stakeRegularFixedRewardSheet.Set(fsv); + stakeRegularRewardSheet.Set(sv); + + return (stakeRegularRewardSheet, stakeRegularFixedRewardSheet); + } + ); Field( "stakeRewards", + deprecationReason: "Since stake3, claim_stake_reward9 actions, each stakers have their own contracts.", resolve: context => { StakeRegularRewardSheet stakeRegularRewardSheet; @@ -362,9 +397,9 @@ public StateQuery() if (context.Source.BlockIndex < StakeState.StakeRewardSheetV2Index) { stakeRegularRewardSheet = new StakeRegularRewardSheet(); - stakeRegularRewardSheet.Set(ClaimStakeReward.V1.StakeRegularRewardSheetCsv); + stakeRegularRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularRewardSheetCsv); stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet(); - stakeRegularFixedRewardSheet.Set(ClaimStakeReward.V1.StakeRegularFixedRewardSheetCsv); + stakeRegularFixedRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularFixedRewardSheetCsv); } else { From 39936ac7da3e2e75144945d43c2883b5ce4dbcd5 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Mon, 28 Aug 2023 13:22:04 +0900 Subject: [PATCH 06/35] Bump lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 45c6a94ab..5be7f5fd2 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 45c6a94ab6c162d5771fa5dabaef160af2307b21 +Subproject commit 5be7f5fd25a093113324700bdb66b94b14408527 From 98c51283a6e9e75b8affd36744b8b3c0c50e87fc Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Mon, 28 Aug 2023 13:22:41 +0900 Subject: [PATCH 07/35] Fix shop test --- NineChronicles.Headless.Tests/Common/Fixtures.cs | 7 +++++-- .../GraphTypes/States/Models/ShopItemTypeTest.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/NineChronicles.Headless.Tests/Common/Fixtures.cs b/NineChronicles.Headless.Tests/Common/Fixtures.cs index c53218f24..59cdc950b 100644 --- a/NineChronicles.Headless.Tests/Common/Fixtures.cs +++ b/NineChronicles.Headless.Tests/Common/Fixtures.cs @@ -53,8 +53,11 @@ public static ShopState ShopStateFX() { var row = TableSheetsFX.EquipmentItemSheet.OrderedList[index]; var equipment = ItemFactory.CreateItemUsable(row, Guid.Empty, 0); - var shopItem = new ShopItem(UserAddress, AvatarAddress, Guid.NewGuid(), index * CurrencyFX, equipment); - shopState.Register(shopItem); + if (equipment is ITradableItem tradableItem) + { + var shopItem = new ShopItem(UserAddress, AvatarAddress, Guid.NewGuid(), index * CurrencyFX, tradableItem); + shopState.Register(shopItem); + } } for (var i = 0; i < TableSheetsFX.CostumeItemSheet.OrderedList.Count; i++) diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/ShopItemTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/ShopItemTypeTest.cs index 787794586..4ff857465 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/ShopItemTypeTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/ShopItemTypeTest.cs @@ -44,7 +44,7 @@ Dictionary costumeDict if (costume is null) { shopItem = new ShopItem(Fixtures.UserAddress, Fixtures.AvatarAddress, - new Guid("d3d9ac06-eb91-4cc4-863a-5b4769ad633e"), 100 * Fixtures.CurrencyFX, itemUsable); + new Guid("d3d9ac06-eb91-4cc4-863a-5b4769ad633e"), 100 * Fixtures.CurrencyFX, (ITradableItem)itemUsable); } else { From 5e8b27703ec28c95a4e0d15b80597994b8e8de25 Mon Sep 17 00:00:00 2001 From: moreal Date: Thu, 31 Aug 2023 07:44:55 +0900 Subject: [PATCH 08/35] Update testcases for obsolete claim_stake_reward8 --- .../Commands/ActionCommandTest.cs | 4 +++- NineChronicles.Headless.Executable/Commands/TxCommand.cs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs index 8721e29b7..fde83dba4 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs @@ -198,7 +198,9 @@ public void ClaimStakeReward(string addressString, int expectedCode) [InlineData(ClaimStakeReward6.ObsoleteBlockIndex, typeof(ClaimStakeReward6))] [InlineData(ClaimStakeReward6.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward7))] [InlineData(ClaimStakeReward7.ObsoleteBlockIndex, typeof(ClaimStakeReward7))] - [InlineData(ClaimStakeReward7.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward))] + [InlineData(ClaimStakeReward7.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward8))] + [InlineData(ClaimStakeReward8.ObsoleteBlockIndex, typeof(ClaimStakeReward8))] + [InlineData(ClaimStakeReward8.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward))] [InlineData(long.MaxValue, typeof(ClaimStakeReward))] public void ClaimStakeRewardWithBlockIndex(long blockIndex, Type expectedActionType) { diff --git a/NineChronicles.Headless.Executable/Commands/TxCommand.cs b/NineChronicles.Headless.Executable/Commands/TxCommand.cs index 235d2a96c..88bef4faa 100644 --- a/NineChronicles.Headless.Executable/Commands/TxCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/TxCommand.cs @@ -76,6 +76,7 @@ public void Sign( nameof(ClaimStakeReward4) => new ClaimStakeReward4(), nameof(ClaimStakeReward5) => new ClaimStakeReward5(), nameof(ClaimStakeReward7) => new ClaimStakeReward7(), + nameof(ClaimStakeReward8) => new ClaimStakeReward8(), nameof(ClaimStakeReward) => new ClaimStakeReward(), nameof(TransferAsset) => new TransferAsset(), nameof(MigrateMonsterCollection) => new MigrateMonsterCollection(), From 157ccbebcdb73f7b1d7fffe5e5a5eaf4936b582f Mon Sep 17 00:00:00 2001 From: moreal Date: Thu, 31 Aug 2023 07:45:09 +0900 Subject: [PATCH 09/35] Bump Lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 4557d10a5..2453c0861 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 4557d10a576793d63563ff77d6e28a13135a00e9 +Subproject commit 2453c08616fb7b818bf854fb29e248b629746cce From 6fd7184b05deaf3d82ed84849bcb759f6f4738cf Mon Sep 17 00:00:00 2001 From: moreal Date: Thu, 31 Aug 2023 10:37:14 +0900 Subject: [PATCH 10/35] Fix tests --- .../Commands/ActionCommandTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs index fde83dba4..a0745ed60 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs @@ -226,8 +226,8 @@ public void ClaimStakeRewardWithBlockIndex(long blockIndex, Type expectedActionT [Theory] [InlineData(0, 0, -1)] - [InlineData(1, 8, 0)] - [InlineData(9, 9, -1)] + [InlineData(1, 9, 0)] + [InlineData(10, 10, -1)] public void ClaimStakeRewardWithActionVersion( int actionVersionMin, int actionVersionMax, From b6600060a38208eea1543c9d764cd4161500a03a Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 31 Aug 2023 11:40:17 +0900 Subject: [PATCH 11/35] Update grinding version --- .../GraphTypes/StandaloneSubscriptionTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs index c632651a9..76f309a1f 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StandaloneSubscriptionTest.cs @@ -75,7 +75,7 @@ public async Task SubscribeTx() { const string query = @" subscription { - tx (actionType: ""grinding"") { + tx (actionType: ""grinding2"") { transaction { id } txResult { blockIndex } } From 7eb52176ac1e3a1a5409ad2cb637b524942c2a86 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 1 Sep 2023 14:03:27 +0900 Subject: [PATCH 12/35] Bump Lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 2453c0861..a1d92a3a1 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 2453c08616fb7b818bf854fb29e248b629746cce +Subproject commit a1d92a3a12e4d93606e6a9dac52a4ed1692406fa From e142d848e6f032d156176543c13d2fc8ef3db5e0 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 1 Sep 2023 14:20:55 +0900 Subject: [PATCH 13/35] Use only StakeStateV2 - Deprecate `StakeStateType.Achievements` --- .../GraphTypes/StateQuery.cs | 19 ++---- .../GraphTypes/States/StakeStateType.cs | 63 ++++--------------- 2 files changed, 15 insertions(+), 67 deletions(-) diff --git a/NineChronicles.Headless/GraphTypes/StateQuery.cs b/NineChronicles.Headless/GraphTypes/StateQuery.cs index 9f1ca1c69..dd361c163 100644 --- a/NineChronicles.Headless/GraphTypes/StateQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StateQuery.cs @@ -242,25 +242,14 @@ public StateQuery() StakeStateType.StakeStateContext? GetStakeState(StateContext ctx, Address agentAddress) { var stakeStateAddress = StakeState.DeriveAddress(agentAddress); - var stakeStateValue = ctx.GetState(stakeStateAddress); - if (stakeStateValue is Dictionary dictionary) + if (ctx.AccountState.TryGetStakeStateV2(stakeStateAddress, out StakeStateV2 stakeStateV2)) { return new StakeStateType.StakeStateContext( - new StakeStateType.StakeStateContext.StakeStateWrapper(new StakeState(dictionary)), + stakeStateV2, + stakeStateAddress, ctx.AccountState, ctx.BlockIndex - ); - } - - if (stakeStateValue is List list) - { - return new StakeStateType.StakeStateContext( - new StakeStateType.StakeStateContext.StakeStateWrapper( - new StakeStateV2(list), - stakeStateAddress), - ctx.AccountState, - ctx.BlockIndex - ); + ); } return null; diff --git a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs index b4b5f4a2f..a5435dbdf 100644 --- a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs +++ b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs @@ -25,56 +25,15 @@ public class StakeStateType : ObjectGraphType { public class StakeStateContext : StateContext { - public class StakeStateWrapper - { - private readonly StakeState? _v1; - private readonly StakeStateV2? _v2; - private readonly Address? _v2Address; - - public StakeStateWrapper(StakeState stakeState) - { - _v1 = stakeState; - _v2 = null; - _v2Address = null; - } - - public StakeStateWrapper(StakeStateV2 stakeStateV2, Address stakeStateV2Address) - { - _v1 = null; - _v2 = stakeStateV2; - _v2Address = stakeStateV2Address; - } - - public long StartedBlockIndex => _v1?.StartedBlockIndex ?? - _v2?.StartedBlockIndex ?? throw new InvalidOperationException(); - - public long ReceivedBlockIndex => _v1?.ReceivedBlockIndex ?? - _v2?.ReceivedBlockIndex ?? throw new InvalidOperationException(); - - public long GetClaimableBlockIndex(long blockIndex) => _v1?.GetClaimableBlockIndex(blockIndex) ?? - _v2?.ClaimableBlockIndex ?? - throw new InvalidOperationException(); - - public long CancellableBlockIndex => _v1?.CancellableBlockIndex ?? - _v2?.CancellableBlockIndex ?? - throw new InvalidOperationException(); - - public StakeState.StakeAchievements? Achievements => _v1?.Achievements; - - public Address Address => _v1?.address ?? - _v2Address ?? - throw new InvalidOperationException(); - - public Contract? Contract => _v2?.Contract; - } - - public StakeStateContext(StakeStateWrapper stakeStateWrapper, IAccountState accountState, long blockIndex) + public StakeStateContext(StakeStateV2 stakeState, Address address, IAccountState accountState, long blockIndex) : base(accountState, blockIndex) { - StakeState = stakeStateWrapper; + StakeState = stakeState; + Address = address; } - public StakeStateWrapper StakeState { get; } + public StakeStateV2 StakeState { get; } + public Address Address { get; } } public StakeStateType() @@ -82,12 +41,12 @@ public StakeStateType() Field>( "address", description: "The address of current state.", - resolve: context => context.Source.StakeState.Address); + resolve: context => context.Source.Address); Field>( "deposit", description: "The staked amount.", resolve: context => context.Source.AccountState.GetBalance( - context.Source.StakeState.Address, + context.Source.Address, new GoldCurrencyState((Dictionary)context.Source.GetState(GoldCurrencyState.Address)!).Currency) .GetQuantityString(true)); Field>( @@ -105,13 +64,13 @@ public StakeStateType() Field>( "claimableBlockIndex", description: "The block index the user can claim rewards.", - resolve: context => context.Source.StakeState.GetClaimableBlockIndex( - context.Source.BlockIndex)); + resolve: context => context.Source.StakeState.ClaimableBlockIndex); Field( nameof(StakeState.Achievements), description: "The staking achievements.", - resolve: context => context.Source.StakeState.Achievements); - Field( + deprecationReason: "Since StakeStateV2, the achievement became removed.", + resolve: _ => null); + Field>( "stakeRewards", resolve: context => { From 5dc2ef1876f7c5f202b451c9c69a6ef972f32cc6 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 1 Sep 2023 14:22:07 +0900 Subject: [PATCH 14/35] Use valid GraphQL types by the fields --- NineChronicles.Headless/GraphTypes/States/StakeStateType.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs index a5435dbdf..50deeb03d 100644 --- a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs +++ b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs @@ -49,11 +49,11 @@ public StakeStateType() context.Source.Address, new GoldCurrencyState((Dictionary)context.Source.GetState(GoldCurrencyState.Address)!).Currency) .GetQuantityString(true)); - Field>( + Field>( "startedBlockIndex", description: "The block index the user started to stake.", resolve: context => context.Source.StakeState.StartedBlockIndex); - Field>( + Field>( "receivedBlockIndex", description: "The block index the user received rewards.", resolve: context => context.Source.StakeState.ReceivedBlockIndex); From 984ab219469f30d1994bad025b27dd1dd5000839 Mon Sep 17 00:00:00 2001 From: hyeon Date: Fri, 1 Sep 2023 14:47:08 +0900 Subject: [PATCH 15/35] Bump lib9c to development --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index d55cbce07..f537ee791 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit d55cbce0706e8443986cdd58b0e2412b17f3def1 +Subproject commit f537ee791eb552350d0bb35a77cad5e89c46fd6d From 40dca5525a13314db466099d8565a06d6f8e5c8d Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 1 Sep 2023 16:28:58 +0900 Subject: [PATCH 16/35] Support Mead in transferAsset actionQuery --- NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs | 1 + NineChronicles.Headless/Utils/CurrencyFactory.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs b/NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs index a3f91ac4b..ef6745ea3 100644 --- a/NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs +++ b/NineChronicles.Headless/GraphTypes/CurrencyEnumType.cs @@ -9,6 +9,7 @@ public enum CurrencyEnum CRYSTAL, NCG, GARAGE, + MEAD } public class CurrencyEnumType : EnumerationGraphType diff --git a/NineChronicles.Headless/Utils/CurrencyFactory.cs b/NineChronicles.Headless/Utils/CurrencyFactory.cs index 3bba7b27c..e40359914 100644 --- a/NineChronicles.Headless/Utils/CurrencyFactory.cs +++ b/NineChronicles.Headless/Utils/CurrencyFactory.cs @@ -32,6 +32,7 @@ public bool TryGetCurrency(string ticker, out Currency currency) var result = ticker switch { "NCG" => GetNCG(), + "MEAD" => Currencies.Mead, _ => Currencies.GetMinterlessCurrency(ticker), }; if (result is null) From a45c5c7397efb9d46df7e5dcc365b605c10e60f1 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 1 Sep 2023 17:02:39 +0900 Subject: [PATCH 17/35] Correct StakeTypeTest testcases --- .../States/Models/StakeStateTypeTest.cs | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs index ad96debf9..d2f91c95d 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs @@ -1,9 +1,12 @@ using System.Collections.Generic; using System.Threading.Tasks; using GraphQL.Execution; +using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume; +using Nekoyume.Model.Stake; using Nekoyume.Model.State; +using Nekoyume.TableData; using NineChronicles.Headless.GraphTypes.States; using NineChronicles.Headless.Tests.Common; using Xunit; @@ -15,7 +18,7 @@ public class StakeStateTypeTest { [Theory] [MemberData(nameof(Members))] - public async Task Query(StakeState stakeState, long deposit, long blockIndex, Dictionary expected) + public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long deposit, long blockIndex, Dictionary expected) { #pragma warning disable CS0618 // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 @@ -38,7 +41,8 @@ public async Task Query(StakeState stakeState, long deposit, long blockIndex, Di var queryResult = await ExecuteQueryAsync( query, source: new StakeStateType.StakeStateContext( - new StakeStateType.StakeStateContext.StakeStateWrapper(stakeState), + stakeState, + stakeStateAddress, mockState, blockIndex)); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; @@ -49,76 +53,83 @@ public async Task Query(StakeState stakeState, long deposit, long blockIndex, Di { new object[] { - new StakeState(Fixtures.StakeStateAddress, 0), + new StakeStateV2( + new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 0), + Fixtures.StakeStateAddress, 100, 0, new Dictionary { ["address"] = Fixtures.StakeStateAddress.ToString(), ["deposit"] = "100.00", - ["startedBlockIndex"] = 0, + ["startedBlockIndex"] = 0L, ["cancellableBlockIndex"] = StakeState.LockupInterval, - ["receivedBlockIndex"] = 0, - ["claimableBlockIndex"] = 0 + StakeState.RewardInterval, + ["receivedBlockIndex"] = 0L, + ["claimableBlockIndex"] = 0L + StakeState.RewardInterval, } }, new object[] { - new StakeState(Fixtures.StakeStateAddress, 100), + new StakeStateV2(new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 100), + Fixtures.StakeStateAddress, 100, 0, new Dictionary { ["address"] = Fixtures.StakeStateAddress.ToString(), ["deposit"] = "100.00", - ["startedBlockIndex"] = 100, + ["startedBlockIndex"] = 100L, ["cancellableBlockIndex"] = 100 + StakeState.LockupInterval, - ["receivedBlockIndex"] = 0, + ["receivedBlockIndex"] = 0L, ["claimableBlockIndex"] = 100 + StakeState.RewardInterval, } }, new object[] { - new StakeState(Fixtures.StakeStateAddress, 100), + new StakeStateV2(new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 100), + Fixtures.StakeStateAddress, 100, 0, new Dictionary { ["address"] = Fixtures.StakeStateAddress.ToString(), ["deposit"] = "100.00", - ["startedBlockIndex"] = 100, + ["startedBlockIndex"] = 100L, ["cancellableBlockIndex"] = StakeState.LockupInterval + 100, - ["receivedBlockIndex"] = 0, + ["receivedBlockIndex"] = 0L, ["claimableBlockIndex"] = StakeState.RewardInterval + 100, } }, new object[] { - new StakeState(Fixtures.StakeStateAddress, 10, 50412, 201610, new StakeState.StakeAchievements()), + new StakeStateV2( + new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 10, 50412), + Fixtures.StakeStateAddress, 100, 0, new Dictionary { ["address"] = Fixtures.StakeStateAddress.ToString(), ["deposit"] = "100.00", - ["startedBlockIndex"] = 10, + ["startedBlockIndex"] = 10L, ["cancellableBlockIndex"] = 201610L, - ["receivedBlockIndex"] = 50412, - ["claimableBlockIndex"] = 100812L, + ["receivedBlockIndex"] = 50412L, + ["claimableBlockIndex"] = 100810L, } }, new object[] { - new StakeState(Fixtures.StakeStateAddress, 10, 50412, 201610, new StakeState.StakeAchievements()), + new StakeStateV2(new Contract("StakeRegularFixedRewardSheet_V1", "StakeRegularRewardSheet_V1", 50400, 201600), 10, 50412), + Fixtures.StakeStateAddress, 100, ActionObsoleteConfig.V100290ObsoleteIndex, new Dictionary { ["address"] = Fixtures.StakeStateAddress.ToString(), ["deposit"] = "100.00", - ["startedBlockIndex"] = 10, + ["startedBlockIndex"] = 10L, ["cancellableBlockIndex"] = 201610L, - ["receivedBlockIndex"] = 50412, + ["receivedBlockIndex"] = 50412L, ["claimableBlockIndex"] = 100810L, } } From c7d6bc5fc17064946a1144f5bf47568e72bece97 Mon Sep 17 00:00:00 2001 From: moreal Date: Fri, 1 Sep 2023 17:06:46 +0900 Subject: [PATCH 18/35] Apply `dotnet format` --- NineChronicles.Headless/GraphTypes/StateQuery.cs | 2 +- NineChronicles.Headless/Utils/CurrencyFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NineChronicles.Headless/GraphTypes/StateQuery.cs b/NineChronicles.Headless/GraphTypes/StateQuery.cs index dd361c163..c46f0f529 100644 --- a/NineChronicles.Headless/GraphTypes/StateQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StateQuery.cs @@ -249,7 +249,7 @@ public StateQuery() stakeStateAddress, ctx.AccountState, ctx.BlockIndex - ); + ); } return null; diff --git a/NineChronicles.Headless/Utils/CurrencyFactory.cs b/NineChronicles.Headless/Utils/CurrencyFactory.cs index e40359914..2c2945bf4 100644 --- a/NineChronicles.Headless/Utils/CurrencyFactory.cs +++ b/NineChronicles.Headless/Utils/CurrencyFactory.cs @@ -32,7 +32,7 @@ public bool TryGetCurrency(string ticker, out Currency currency) var result = ticker switch { "NCG" => GetNCG(), - "MEAD" => Currencies.Mead, + "MEAD" => Currencies.Mead, _ => Currencies.GetMinterlessCurrency(ticker), }; if (result is null) From 04db41ee91ef076fe576ff5a29e5a1be7e1e197f Mon Sep 17 00:00:00 2001 From: moreal Date: Mon, 4 Sep 2023 15:16:14 +0900 Subject: [PATCH 19/35] Fix misusage of TryGetStakeStateV2 --- NineChronicles.Headless/GraphTypes/StateQuery.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NineChronicles.Headless/GraphTypes/StateQuery.cs b/NineChronicles.Headless/GraphTypes/StateQuery.cs index c46f0f529..8ce7407b8 100644 --- a/NineChronicles.Headless/GraphTypes/StateQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StateQuery.cs @@ -242,7 +242,7 @@ public StateQuery() StakeStateType.StakeStateContext? GetStakeState(StateContext ctx, Address agentAddress) { var stakeStateAddress = StakeState.DeriveAddress(agentAddress); - if (ctx.AccountState.TryGetStakeStateV2(stakeStateAddress, out StakeStateV2 stakeStateV2)) + if (ctx.AccountState.TryGetStakeStateV2(agentAddr: agentAddress, out StakeStateV2 stakeStateV2)) { return new StakeStateType.StakeStateContext( stakeStateV2, From 05e3886b9d337be80321c6b8363f6376204ec697 Mon Sep 17 00:00:00 2001 From: area363 Date: Thu, 31 Aug 2023 23:38:29 +0900 Subject: [PATCH 20/35] bump lib9c bump: Lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index f537ee791..a15e2151f 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit f537ee791eb552350d0bb35a77cad5e89c46fd6d +Subproject commit a15e2151f93ede2da1a6402b097a833b348a89d7 From 28f36d2b93cfb56230702ef868477df450fd702f Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 5 Sep 2023 14:56:57 +0900 Subject: [PATCH 21/35] Accommodate API changes --- .../Hosting/LibplanetNodeServiceTest.cs | 2 +- Libplanet.Headless/ReducedStore.cs | 1 - .../Commands/ReplayCommand.Privates.cs | 36 +++++++------- .../Commands/ReplayCommand.cs | 2 +- .../Commands/StateCommand.cs | 2 +- .../Common/Actions/EmptyAction.cs | 6 +-- .../GraphTypes/StandaloneQuery.cs | 49 ++++++++----------- .../GraphTypes/StandaloneSubscription.cs | 4 -- .../GraphTypes/TransactionHeadlessQuery.cs | 10 ++-- 9 files changed, 48 insertions(+), 64 deletions(-) diff --git a/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs b/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs index 85d414fb5..938e5e305 100644 --- a/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs +++ b/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs @@ -88,7 +88,7 @@ private class DummyAction : IAction { IValue IAction.PlainValue => Dictionary.Empty; - IAccountStateDelta IAction.Execute(IActionContext context) + IAccount IAction.Execute(IActionContext context) { return context.PreviousState; } diff --git a/Libplanet.Headless/ReducedStore.cs b/Libplanet.Headless/ReducedStore.cs index e55be9d65..389597d0b 100644 --- a/Libplanet.Headless/ReducedStore.cs +++ b/Libplanet.Headless/ReducedStore.cs @@ -113,7 +113,6 @@ public void PutTxExecution(TxSuccess txSuccess) txSuccess.BlockHash, txSuccess.TxId, updatedStates: txSuccess.UpdatedStates.ToImmutableDictionary(pair => pair.Key, _ => (IValue)Null.Value), - fungibleAssetsDelta: txSuccess.FungibleAssetsDelta, updatedFungibleAssets: txSuccess.UpdatedFungibleAssets ); InternalStore.PutTxExecution(reducedTxSuccess); diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs index 05046254b..24e371342 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs @@ -29,7 +29,7 @@ public partial class ReplayCommand : CoconaLiteConsoleAppBase /// Almost duplicate https://github.com/planetarium/libplanet/blob/main/Libplanet/State/AccountStateDelta.cs. /// [Pure] - private sealed class AccountStateDelta : IAccountStateDelta + private sealed class AccountStateDelta : IAccount { private readonly IAccountState _baseState; @@ -97,7 +97,7 @@ private AccountStateDelta(IAccountState baseState, IAccountDelta delta) /// [Pure] - public IAccountStateDelta SetState(Address address, IValue state) => + public IAccount SetState(Address address, IValue state) => UpdateStates(Delta.States.SetItem(address, state)); /// @@ -132,7 +132,7 @@ public ValidatorSet GetValidatorSet() => /// [Pure] - public IAccountStateDelta MintAsset( + public IAccount MintAsset( IActionContext context, Address recipient, FungibleAssetValue value) { if (value.Sign <= 0) @@ -183,7 +183,7 @@ public IAccountStateDelta MintAsset( /// [Pure] - public IAccountStateDelta TransferAsset( + public IAccount TransferAsset( IActionContext context, Address sender, Address recipient, @@ -194,7 +194,7 @@ public IAccountStateDelta TransferAsset( /// [Pure] - public IAccountStateDelta BurnAsset( + public IAccount BurnAsset( IActionContext context, Address owner, FungibleAssetValue value) { string msg; @@ -245,7 +245,7 @@ public IAccountStateDelta BurnAsset( /// [Pure] - public IAccountStateDelta SetValidator(Validator validator) + public IAccount SetValidator(Validator validator) { return UpdateValidatorSet(GetValidatorSet().Update(validator)); } @@ -257,14 +257,14 @@ public IAccountStateDelta SetValidator(Validator validator) /// a basis. /// A null state delta created from . /// - internal static IAccountStateDelta Create(IAccountState previousState) => + internal static IAccount Create(IAccountState previousState) => new AccountStateDelta(previousState); /// /// Creates a null state delta while inheriting s /// total updated fungibles. /// - /// The previous to use. + /// The previous to use. /// A null state delta that is of the same type as . /// /// Thrown if given @@ -272,9 +272,9 @@ internal static IAccountStateDelta Create(IAccountState previousState) => /// /// /// This inherits 's - /// . + /// . /// - internal static IAccountStateDelta Flush(IAccountStateDelta stateDelta) => + internal static IAccount Flush(IAccount stateDelta) => stateDelta is AccountStateDelta impl ? new AccountStateDelta(stateDelta) { @@ -348,7 +348,7 @@ private AccountStateDelta UpdateValidatorSet( }; [Pure] - private IAccountStateDelta TransferAssetV0( + private IAccount TransferAssetV0( Address sender, Address recipient, FungibleAssetValue value, @@ -384,7 +384,7 @@ private IAccountStateDelta TransferAssetV0( } [Pure] - private IAccountStateDelta TransferAssetV1( + private IAccount TransferAssetV1( Address sender, Address recipient, FungibleAssetValue value, @@ -501,7 +501,7 @@ public ActionContext( Address miner, long blockIndex, int blockProtocolVersion, - IAccountStateDelta previousState, + IAccount previousState, int randomSeed, bool rehearsal = false) { @@ -528,7 +528,7 @@ public ActionContext( public bool Rehearsal { get; } - public IAccountStateDelta PreviousState { get; } + public IAccount PreviousState { get; } public IRandom Random { get; } @@ -746,7 +746,7 @@ private static IEnumerable EvaluateActions( long blockIndex, int blockProtocolVersion, TxId? txid, - IAccountStateDelta previousStates, + IAccount previousStates, Address miner, Address signer, byte[] signature, @@ -754,7 +754,7 @@ private static IEnumerable EvaluateActions( ILogger? logger = null) { ActionContext CreateActionContext( - IAccountStateDelta prevState, + IAccount prevState, int randomSeed) { return new ActionContext( @@ -776,11 +776,11 @@ ActionContext CreateActionContext( byte[] preEvaluationHashBytes = preEvaluationHash.ToByteArray(); int seed = ActionEvaluator.GenerateRandomSeed(preEvaluationHashBytes, hashedSignature, signature, 0); - IAccountStateDelta states = previousStates; + IAccount states = previousStates; foreach (IAction action in actions) { Exception? exc = null; - IAccountStateDelta nextStates = states; + IAccount nextStates = states; ActionContext context = CreateActionContext(nextStates, seed); try diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs index 02a7f1218..0f8f70f6e 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs @@ -101,7 +101,7 @@ public int Tx( // Evaluate tx. IAccountState previousBlockStates = blockChain.GetBlockState(previousBlock.Hash); - IAccountStateDelta previousStates = AccountStateDelta.Create(previousBlockStates); + IAccount previousStates = AccountStateDelta.Create(previousBlockStates); var actions = tx.Actions.Select(a => ToAction(a)); var actionEvaluations = EvaluateActions( preEvaluationHash: targetBlock.PreEvaluationHash, diff --git a/NineChronicles.Headless.Executable/Commands/StateCommand.cs b/NineChronicles.Headless.Executable/Commands/StateCommand.cs index 513a7e90b..235e7bbd8 100644 --- a/NineChronicles.Headless.Executable/Commands/StateCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/StateCommand.cs @@ -461,7 +461,7 @@ private static ImmutableDictionary GetTotalDelta( return ImmutableDictionary.Empty; } - IAccountStateDelta lastStates = actionEvaluations[actionEvaluations.Count - 1].OutputState; + IAccount lastStates = actionEvaluations[actionEvaluations.Count - 1].OutputState; ImmutableDictionary totalDelta = stateUpdatedAddresses.ToImmutableDictionary( diff --git a/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs b/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs index de3b21288..a33c0ecb2 100644 --- a/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs +++ b/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs @@ -11,12 +11,12 @@ public void LoadPlainValue(IValue plainValue) { } - public IAccountStateDelta Execute(IActionContext context) + public IAccount Execute(IActionContext context) { return context.PreviousState; } - public void Render(IActionContext context, IAccountStateDelta nextStates) + public void Render(IActionContext context, IAccount nextStates) { } @@ -24,7 +24,7 @@ public void RenderError(IActionContext context, Exception exception) { } - public void Unrender(IActionContext context, IAccountStateDelta nextStates) + public void Unrender(IActionContext context, IAccount nextStates) { } diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs index 87667f91f..95ad54498 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs @@ -116,39 +116,32 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi var recipient = context.GetArgument("recipient"); IEnumerable txs = digest.TxIds - .Select(b => new TxId(b.ToBuilder().ToArray())) + .Select(bytes => new TxId(bytes)) .Select(store.GetTransaction); - var filteredTransactions = txs.Where(tx => - tx.Actions!.Count == 1 && - ToAction(tx.Actions.First()) is ITransferAsset transferAsset && - (!recipient.HasValue || transferAsset.Recipient == recipient) && - transferAsset.Amount.Currency.Ticker == "NCG" && - store.GetTxExecution(blockHash, tx.Id) is TxSuccess); - - TransferNCGHistory ToTransferNCGHistory(TxSuccess txSuccess, string? memo) + + var pairs = txs + .Where(tx => + tx.Actions!.Count == 1 && + store.GetTxExecution(blockHash, tx.Id) is TxSuccess) + .Select(tx => (tx.Id, ToAction(tx.Actions.First()))) + .Where(pair => + pair.Item2 is ITransferAsset transferAssset && + transferAssset.Amount.Currency.Ticker == "NCG") + .Select(pair => (pair.Item1, (ITransferAsset)pair.Item2)) + .Where(pair => (!(recipient is { } r) || pair.Item2.Recipient == r)); + + TransferNCGHistory ToTransferNCGHistory((TxId TxId, ITransferAsset Transfer) pair) { - var rawTransferNcgHistories = txSuccess.FungibleAssetsDelta - .Where(pair => pair.Value.Values.Any(fav => fav.Currency.Ticker == "NCG")) - .Select(pair => - (pair.Key, pair.Value.Values.First(fav => fav.Currency.Ticker == "NCG"))) - .ToArray(); - var ((senderAddress, _), (recipientAddress, amount)) = - rawTransferNcgHistories[0].Item2.RawValue > rawTransferNcgHistories[1].Item2.RawValue - ? (rawTransferNcgHistories[1], rawTransferNcgHistories[0]) - : (rawTransferNcgHistories[0], rawTransferNcgHistories[1]); return new TransferNCGHistory( - txSuccess.BlockHash, - txSuccess.TxId, - senderAddress, - recipientAddress, - amount, - memo); + blockHash, + pair.TxId, + pair.Transfer.Sender, + pair.Transfer.Recipient, + pair.Transfer.Amount, + pair.Transfer.Memo); } - var histories = filteredTransactions.Select(tx => - ToTransferNCGHistory((TxSuccess)store.GetTxExecution(blockHash, tx.Id), - ((ITransferAsset)ToAction(tx.Actions!.Single())).Memo)); - + var histories = pairs.Select(ToTransferNCGHistory); return histories; }); diff --git a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs index 4b699c65e..4fdf34bac 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs @@ -329,21 +329,17 @@ private IObservable SubscribeTx(IResolveFieldContext context) txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), null, - null, success.UpdatedStates .Select(kv => new KeyValuePair( kv.Key, kv.Value)) .ToImmutableDictionary(), - success.FungibleAssetsDelta, success.UpdatedFungibleAssets), TxFailure failure => new TxResult( TxStatus.FAILURE, txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), failure.ExceptionName, - failure.ExceptionMetadata, - null, null, null), _ => null diff --git a/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs b/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs index fda11309a..ecaa294ee 100644 --- a/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs +++ b/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs @@ -207,8 +207,8 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext) if (!(store.GetFirstTxIdBlockHashIndex(txId) is { } txExecutedBlockHash)) { return blockChain.GetStagedTransactionIds().Contains(txId) - ? new TxResult(TxStatus.STAGING, null, null, null, null, null, null, null) - : new TxResult(TxStatus.INVALID, null, null, null, null, null, null, null); + ? new TxResult(TxStatus.STAGING, null, null, null, null, null) + : new TxResult(TxStatus.INVALID, null, null, null, null, null); } try @@ -222,21 +222,17 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext) txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), null, - null, txSuccess.UpdatedStates .Select(kv => new KeyValuePair( kv.Key, kv.Value)) .ToImmutableDictionary(), - txSuccess.FungibleAssetsDelta, txSuccess.UpdatedFungibleAssets), TxFailure txFailure => new TxResult( TxStatus.FAILURE, txExecutedBlock.Index, txExecutedBlock.Hash.ToString(), txFailure.ExceptionName, - txFailure.ExceptionMetadata, - null, null, null), _ => throw new NotImplementedException( @@ -245,7 +241,7 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext) } catch (Exception) { - return new TxResult(TxStatus.INVALID, null, null, null, null, null, null, null); + return new TxResult(TxStatus.INVALID, null, null, null, null, null); } } ); From 0179411b73748dee6c77c6ccdb1a3187418b8b6f Mon Sep 17 00:00:00 2001 From: moreal Date: Wed, 6 Sep 2023 07:07:14 +0900 Subject: [PATCH 22/35] Bump lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index b4fbea61c..9fb67c2f0 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit b4fbea61c9adbe9d70640075e01ab37503e92197 +Subproject commit 9fb67c2f0f4c96f71068eae86e72972852eb8867 From b241849a330af59df3ae2af55ca5c3c82e67d2d8 Mon Sep 17 00:00:00 2001 From: moreal Date: Tue, 5 Sep 2023 08:01:27 +0900 Subject: [PATCH 23/35] fix(graphql): return null instead of errors --- NineChronicles.Headless/GraphTypes/StandaloneQuery.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs index 95ad54498..ee5648bbf 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs @@ -84,6 +84,11 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi var state = blockChain.GetStates(new[] { address }, blockHash)[0]; + if (state is null) + { + return null; + } + return new Codec().Encode(state); } ); From 19461c11699b9d9259fadfad3191b0d3671ff05d Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 6 Sep 2023 11:52:32 +0900 Subject: [PATCH 24/35] Introduce auraSummon ActionQuery --- .../GraphTypes/ActionQueryTest.cs | 29 +++++++++++++ .../GraphTypes/ActionQuery.cs | 1 + .../GraphTypes/ActionQueryFields/Summon.cs | 43 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 NineChronicles.Headless/GraphTypes/ActionQueryFields/Summon.cs diff --git a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs index b964d7416..8e720b2ce 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs @@ -1311,5 +1311,34 @@ private static IEnumerable GetMemberDataOfUnloadFromMyGarages() "memo", }; } + + [Fact] + public async Task AuraSummon() + { + var random = new Random(); + var avatarAddress = new PrivateKey().ToAddress(); + var groupId = random.Next(10001, 10002 + 1); + // FIXME: Change 10 to AuraSummon.SummonLimit + var summonCount = random.Next(1, 10 + 1); + + var query = $@"{{ + auraSummon( + avatarAddress: ""{avatarAddress}"", + groupId: {groupId}, + summonCount: {summonCount} + ) + }}"; + + var queryResult = await ExecuteQueryAsync(query, standaloneContext: _standaloneContext); + var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; + var plainValue = _codec.Decode(ByteUtil.ParseHex((string)data["auraSummon"])); + Assert.IsType(plainValue); + var actionBase = DeserializeNCAction(plainValue); + var action = Assert.IsType(actionBase); + + Assert.Equal(avatarAddress, action.AvatarAddress); + Assert.Equal(groupId, action.GroupId); + Assert.Equal(summonCount, action.SummonCount); + } } } diff --git a/NineChronicles.Headless/GraphTypes/ActionQuery.cs b/NineChronicles.Headless/GraphTypes/ActionQuery.cs index 207be91b5..a3e583954 100644 --- a/NineChronicles.Headless/GraphTypes/ActionQuery.cs +++ b/NineChronicles.Headless/GraphTypes/ActionQuery.cs @@ -542,6 +542,7 @@ public ActionQuery(StandaloneContext standaloneContext) RegisterCombinationConsumable(); RegisterMead(); RegisterGarages(); + RegisterSummon(); Field>( name: "craftQuery", diff --git a/NineChronicles.Headless/GraphTypes/ActionQueryFields/Summon.cs b/NineChronicles.Headless/GraphTypes/ActionQueryFields/Summon.cs new file mode 100644 index 000000000..6391f7eaf --- /dev/null +++ b/NineChronicles.Headless/GraphTypes/ActionQueryFields/Summon.cs @@ -0,0 +1,43 @@ +using GraphQL; +using GraphQL.Types; +using Libplanet.Crypto; +using Libplanet.Explorer.GraphTypes; +using Nekoyume.Action; + +namespace NineChronicles.Headless.GraphTypes; + +public partial class ActionQuery +{ + private void RegisterSummon() + { + Field>( + "auraSummon", + arguments: new QueryArguments( + new QueryArgument> + { + Name = "avatarAddress", + Description = "Avatar address to get summoned items" + }, + new QueryArgument> + { + Name = "groupId", + Description = "Summon group id" + }, + new QueryArgument> + { + Name = "summonCount", + Description = "Count to summon. Must between 1 and 10." + } + ), + resolve: context => + { + var avatarAddr = context.GetArgument
("avatarAddress"); + var groupId = context.GetArgument("groupId"); + var summonCount = context.GetArgument("summonCount"); + + ActionBase action = new AuraSummon(avatarAddr, groupId, summonCount); + return Encode(context, action); + } + ); + } +} From 9bac47ec751be5df6e65a67d9edd9bd91995c4c4 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 6 Sep 2023 17:34:43 +0900 Subject: [PATCH 25/35] Bump lib9c: Apply refactoring --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 9fb67c2f0..75ef95354 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 9fb67c2f0f4c96f71068eae86e72972852eb8867 +Subproject commit 75ef95354a4a290687be22d1b463aa2752840c94 From 9d8625b3a596b84f61a4204488764b21c3df8c0d Mon Sep 17 00:00:00 2001 From: Atralupus Date: Thu, 7 Sep 2023 02:19:47 +0900 Subject: [PATCH 26/35] Add .gitallowed for git secrets --- .gitallowed | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .gitallowed diff --git a/.gitallowed b/.gitallowed new file mode 100644 index 000000000..0bd84f123 --- /dev/null +++ b/.gitallowed @@ -0,0 +1,19 @@ +# For BUILD_GENESIS.md +e87a05d05506b73570e80f6e99beeceae6a9891333b2e8e8951197050fad96e2 + +# For ConversionCommandTest.cs +a1262b4f0911a9cec5e64344c0c9b50d64f8781ade0e09fa79faaa127ccdff89 + +# For ValidationCommandTest.cs +ab8d591ccdcce263c39eb1f353e44b64869f0afea2df643bf6839ebde650d244 +d6c3e0d525dac340a132ae05aaa9f3e278d61b70d2b71326570e64aee249e566 +761f68d68426549df5904395b5ca5bce64a3da759085d8565242db42a5a1b0b9 + +# For Fixtures.cs +b934cb79757b1dec9f89caa01c4b791a6de6937dbecdc102fbdca217156cc2f5 + +# For AddressQueryTest.cs +b8ce43967d7270348906c3b30efd41c30ab834ce07a36ee8ac5fd52cb7a3f579 + +# For StandaloneQueryTest.cs +9330b3287bd2bbc38770c69ae7cd380350c60a1dff9ec41254f3048d5b3eb01c From b34839034279057ed2030c4d6898857f4a230c97 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Fri, 8 Sep 2023 13:12:05 +0900 Subject: [PATCH 27/35] Bump lib9c to 6a5fd2f4 --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 75ef95354..6a5fd2f48 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 75ef95354a4a290687be22d1b463aa2752840c94 +Subproject commit 6a5fd2f48852d26dcd6b168c294813f30575da7a From 4aa125d4c7dd077f9cfe1967f80ded1042d09ce3 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 6 Sep 2023 12:11:24 +0900 Subject: [PATCH 28/35] Accommodate API changes --- .../Commands/ChainCommandTest.cs | 2 +- .../Commands/ReplayCommand.Privates.cs | 509 +----------------- .../Commands/ReplayCommand.cs | 7 +- .../Common/MockState.cs | 3 + .../GraphQLTestUtils.cs | 4 +- .../TransactionHeadlessQueryTest.cs | 2 +- NineChronicles.Headless/BlockChainService.cs | 2 +- .../GraphTypes/StandaloneQuery.cs | 2 +- .../NineChroniclesNodeService.cs | 3 +- 9 files changed, 39 insertions(+), 495 deletions(-) diff --git a/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs index 893ddef93..17f04ca9a 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs @@ -255,7 +255,7 @@ public void PruneState(StoreType storeType) _command.PruneStates(storeType, _storePath); IStore outputStore = storeType.CreateStore(_storePath); var outputStateKeyValueStore = new RocksDBKeyValueStore(statesPath); - var outputStateStore = new TrieStateStore(outputStateKeyValueStore, true); + var outputStateStore = new TrieStateStore(outputStateKeyValueStore); int outputStatesCount = outputStateKeyValueStore.ListKeys().Count(); outputStore.Dispose(); outputStateStore.Dispose(); diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs index 24e371342..9581815f3 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs @@ -2,10 +2,8 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.IO; using System.Linq; -using System.Numerics; using System.Security.Cryptography; using Bencodex; using Bencodex.Types; @@ -13,481 +11,19 @@ using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Types.Assets; using Libplanet.Types.Consensus; -using Libplanet.Action.State; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; using RocksDbSharp; using Serilog; +using Libplanet.Store.Trie; namespace NineChronicles.Headless.Executable.Commands { public partial class ReplayCommand : CoconaLiteConsoleAppBase { - /// - /// Almost duplicate https://github.com/planetarium/libplanet/blob/main/Libplanet/State/AccountStateDelta.cs. - /// - [Pure] - private sealed class AccountStateDelta : IAccount - { - private readonly IAccountState _baseState; - - private AccountStateDelta(IAccountState baseState) - : this(baseState, new AccountDelta()) - { - } - - private AccountStateDelta(IAccountState baseState, IAccountDelta delta) - { - _baseState = baseState; - Delta = delta; - TotalUpdatedFungibles = ImmutableDictionary<(Address, Currency), BigInteger>.Empty; - } - - /// - public IAccountDelta Delta { get; private set; } - - /// - public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets => - TotalUpdatedFungibles.Keys.ToImmutableHashSet(); - - public IImmutableDictionary<(Address, Currency), BigInteger> TotalUpdatedFungibles { get; private set; } - - /// - [Pure] - public IValue? GetState(Address address) - { - IValue? state = GetStates(new[] { address })[0]; - return state; - } - - /// - [Pure] - public IReadOnlyList GetStates(IReadOnlyList
addresses) - { - int length = addresses.Count; - IValue?[] values = new IValue?[length]; - var notFoundIndices = new List(length); - for (int i = 0; i < length; i++) - { - Address address = addresses[i]; - if (Delta.States.TryGetValue(address, out IValue? updatedValue)) - { - values[i] = updatedValue; - } - else - { - notFoundIndices.Add(i); - } - } - - if (notFoundIndices.Count > 0) - { - IReadOnlyList restValues = _baseState.GetStates( - notFoundIndices.Select(index => addresses[index]).ToArray()); - foreach ((var v, var i) in notFoundIndices.Select((v, i) => (v, i))) - { - values[v] = restValues[i]; - } - } - - return values; - } - - /// - [Pure] - public IAccount SetState(Address address, IValue state) => - UpdateStates(Delta.States.SetItem(address, state)); - - /// - [Pure] - public FungibleAssetValue GetBalance(Address address, Currency currency) => - GetBalance(address, currency, Delta.Fungibles); - - /// - [Pure] - public FungibleAssetValue GetTotalSupply(Currency currency) - { - if (!currency.TotalSupplyTrackable) - { - throw new TotalSupplyNotTrackableException( - $"Given currency {currency} is not trackable for its total supply", - currency); - } - - // Return dirty state if it exists. - if (Delta.TotalSupplies.TryGetValue(currency, out BigInteger totalSupplyValue)) - { - return FungibleAssetValue.FromRawValue(currency, totalSupplyValue); - } - - return _baseState.GetTotalSupply(currency); - } - - /// - [Pure] - public ValidatorSet GetValidatorSet() => - Delta.ValidatorSet ?? _baseState.GetValidatorSet(); - - /// - [Pure] - public IAccount MintAsset( - IActionContext context, Address recipient, FungibleAssetValue value) - { - if (value.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(value), - "The value to mint has to be greater than zero." - ); - } - - Currency currency = value.Currency; - if (!currency.AllowsToMint(context.Signer)) - { - throw new CurrencyPermissionException( - $"The account {context.Signer} has no permission to mint currency {currency}.", - context.Signer, - currency - ); - } - - FungibleAssetValue balance = GetBalance(recipient, currency); - (Address, Currency) assetKey = (recipient, currency); - BigInteger rawBalance = (balance + value).RawValue; - - if (currency.TotalSupplyTrackable) - { - var currentTotalSupply = GetTotalSupply(currency); - if (currency.MaximumSupply < currentTotalSupply + value) - { - var msg = $"The amount {value} attempted to be minted added to the current" - + $" total supply of {currentTotalSupply} exceeds the" - + $" maximum allowed supply of {currency.MaximumSupply}."; - throw new SupplyOverflowException(msg, value); - } - - return UpdateFungibleAssets( - Delta.Fungibles.SetItem(assetKey, rawBalance), - TotalUpdatedFungibles.SetItem(assetKey, rawBalance), - Delta.TotalSupplies.SetItem(currency, (currentTotalSupply + value).RawValue) - ); - } - - return UpdateFungibleAssets( - Delta.Fungibles.SetItem(assetKey, rawBalance), - TotalUpdatedFungibles.SetItem(assetKey, rawBalance) - ); - } - - /// - [Pure] - public IAccount TransferAsset( - IActionContext context, - Address sender, - Address recipient, - FungibleAssetValue value, - bool allowNegativeBalance = false) => context.BlockProtocolVersion > 0 - ? TransferAssetV1(sender, recipient, value, allowNegativeBalance) - : TransferAssetV0(sender, recipient, value, allowNegativeBalance); - - /// - [Pure] - public IAccount BurnAsset( - IActionContext context, Address owner, FungibleAssetValue value) - { - string msg; - - if (value.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(value), - "The value to burn has to be greater than zero." - ); - } - - Currency currency = value.Currency; - if (!currency.AllowsToMint(context.Signer)) - { - msg = $"The account {context.Signer} has no permission to burn assets of " + - $"the currency {currency}."; - throw new CurrencyPermissionException(msg, context.Signer, currency); - } - - FungibleAssetValue balance = GetBalance(owner, currency); - - if (balance < value) - { - msg = $"The account {owner}'s balance of {currency} is insufficient to burn: " + - $"{balance} < {value}."; - throw new InsufficientBalanceException(msg, owner, balance); - } - - (Address, Currency) assetKey = (owner, currency); - BigInteger rawBalance = (balance - value).RawValue; - if (currency.TotalSupplyTrackable) - { - return UpdateFungibleAssets( - Delta.Fungibles.SetItem(assetKey, rawBalance), - TotalUpdatedFungibles.SetItem(assetKey, rawBalance), - Delta.TotalSupplies.SetItem( - currency, - (GetTotalSupply(currency) - value).RawValue) - ); - } - - return UpdateFungibleAssets( - Delta.Fungibles.SetItem(assetKey, rawBalance), - TotalUpdatedFungibles.SetItem(assetKey, rawBalance) - ); - } - - /// - [Pure] - public IAccount SetValidator(Validator validator) - { - return UpdateValidatorSet(GetValidatorSet().Update(validator)); - } - - /// - /// Creates a null state delta from given . - /// - /// The previous to use as - /// a basis. - /// A null state delta created from . - /// - internal static IAccount Create(IAccountState previousState) => - new AccountStateDelta(previousState); - - /// - /// Creates a null state delta while inheriting s - /// total updated fungibles. - /// - /// The previous to use. - /// A null state delta that is of the same type as . - /// - /// Thrown if given - /// is not . - /// - /// - /// This inherits 's - /// . - /// - internal static IAccount Flush(IAccount stateDelta) => - stateDelta is AccountStateDelta impl - ? new AccountStateDelta(stateDelta) - { - TotalUpdatedFungibles = impl.TotalUpdatedFungibles, - } - : throw new ArgumentException( - $"Unknown type for {nameof(stateDelta)}: {stateDelta.GetType()}"); - - [Pure] - private FungibleAssetValue GetBalance( - Address address, - Currency currency, - IImmutableDictionary<(Address, Currency), BigInteger> balances) => - balances.TryGetValue((address, currency), out BigInteger balance) - ? FungibleAssetValue.FromRawValue(currency, balance) - : _baseState.GetBalance(address, currency); - - [Pure] - private AccountStateDelta UpdateStates( - IImmutableDictionary updatedStates) => - new AccountStateDelta( - _baseState, - new AccountDelta( - updatedStates, - Delta.Fungibles, - Delta.TotalSupplies, - Delta.ValidatorSet)) - { - TotalUpdatedFungibles = TotalUpdatedFungibles, - }; - - [Pure] - private AccountStateDelta UpdateFungibleAssets( - IImmutableDictionary<(Address, Currency), BigInteger> updatedFungibleAssets, - IImmutableDictionary<(Address, Currency), BigInteger> totalUpdatedFungibles - ) => - UpdateFungibleAssets( - updatedFungibleAssets, - totalUpdatedFungibles, - Delta.TotalSupplies); - - [Pure] - private AccountStateDelta UpdateFungibleAssets( - IImmutableDictionary<(Address, Currency), BigInteger> updatedFungibleAssets, - IImmutableDictionary<(Address, Currency), BigInteger> totalUpdatedFungibles, - IImmutableDictionary updatedTotalSupply - ) => - new AccountStateDelta( - _baseState, - new AccountDelta( - Delta.States, - updatedFungibleAssets, - updatedTotalSupply, - Delta.ValidatorSet)) - { - TotalUpdatedFungibles = totalUpdatedFungibles, - }; - - [Pure] - private AccountStateDelta UpdateValidatorSet( - ValidatorSet updatedValidatorSet) => - new AccountStateDelta( - _baseState, - new AccountDelta( - Delta.States, - Delta.Fungibles, - Delta.TotalSupplies, - updatedValidatorSet)) - { - TotalUpdatedFungibles = TotalUpdatedFungibles, - }; - - [Pure] - private IAccount TransferAssetV0( - Address sender, - Address recipient, - FungibleAssetValue value, - bool allowNegativeBalance = false) - { - if (value.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(value), - "The value to transfer has to be greater than zero." - ); - } - - Currency currency = value.Currency; - FungibleAssetValue senderBalance = GetBalance(sender, currency); - FungibleAssetValue recipientBalance = GetBalance(recipient, currency); - - if (!allowNegativeBalance && senderBalance < value) - { - var msg = $"The account {sender}'s balance of {currency} is insufficient to " + - $"transfer: {senderBalance} < {value}."; - throw new InsufficientBalanceException(msg, sender, senderBalance); - } - - return UpdateFungibleAssets( - Delta.Fungibles - .SetItem((sender, currency), (senderBalance - value).RawValue) - .SetItem((recipient, currency), (recipientBalance + value).RawValue), - TotalUpdatedFungibles - .SetItem((sender, currency), (senderBalance - value).RawValue) - .SetItem((recipient, currency), (recipientBalance + value).RawValue) - ); - } - - [Pure] - private IAccount TransferAssetV1( - Address sender, - Address recipient, - FungibleAssetValue value, - bool allowNegativeBalance = false) - { - if (value.Sign <= 0) - { - throw new ArgumentOutOfRangeException( - nameof(value), - "The value to transfer has to be greater than zero." - ); - } - - Currency currency = value.Currency; - FungibleAssetValue senderBalance = GetBalance(sender, currency); - - if (!allowNegativeBalance && senderBalance < value) - { - var msg = $"The account {sender}'s balance of {currency} is insufficient to " + - $"transfer: {senderBalance} < {value}."; - throw new InsufficientBalanceException(msg, sender, senderBalance); - } - - (Address, Currency) senderAssetKey = (sender, currency); - BigInteger senderRawBalance = (senderBalance - value).RawValue; - - IImmutableDictionary<(Address, Currency), BigInteger> updatedFungibleAssets = - Delta.Fungibles.SetItem(senderAssetKey, senderRawBalance); - IImmutableDictionary<(Address, Currency), BigInteger> totalUpdatedFungibles = - TotalUpdatedFungibles.SetItem(senderAssetKey, senderRawBalance); - - FungibleAssetValue recipientBalance = GetBalance( - recipient, - currency, - updatedFungibleAssets); - (Address, Currency) recipientAssetKey = (recipient, currency); - BigInteger recipientRawBalance = (recipientBalance + value).RawValue; - - return UpdateFungibleAssets( - updatedFungibleAssets.SetItem(recipientAssetKey, recipientRawBalance), - totalUpdatedFungibles.SetItem(recipientAssetKey, recipientRawBalance) - ); - } - } - - /// - /// Almost duplicate https://github.com/planetarium/libplanet/blob/main/Libplanet/State/AccountDelta.cs. - /// - private sealed class AccountDelta : IAccountDelta - { - internal AccountDelta() - { - States = ImmutableDictionary.Empty; - Fungibles = ImmutableDictionary<(Address, Currency), BigInteger>.Empty; - TotalSupplies = ImmutableDictionary.Empty; - ValidatorSet = null; - } - - internal AccountDelta( - IImmutableDictionary statesDelta, - IImmutableDictionary<(Address, Currency), BigInteger> fungiblesDelta, - IImmutableDictionary totalSuppliesDelta, - ValidatorSet? validatorSetDelta) - { - States = statesDelta; - Fungibles = fungiblesDelta; - TotalSupplies = totalSuppliesDelta; - ValidatorSet = validatorSetDelta; - } - - /// - public IImmutableSet
UpdatedAddresses => - StateUpdatedAddresses.Union(FungibleUpdatedAddresses); - - /// - public IImmutableSet
StateUpdatedAddresses => - States.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary States { get; } - - /// - public IImmutableSet
FungibleUpdatedAddresses => - Fungibles.Keys.Select(pair => pair.Item1).ToImmutableHashSet(); - - /// - public IImmutableSet<(Address, Currency)> UpdatedFungibleAssets => - Fungibles.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary<(Address, Currency), BigInteger> Fungibles { get; } - - /// - public IImmutableSet UpdatedTotalSupplyCurrencies => - TotalSupplies.Keys.ToImmutableHashSet(); - - /// - public IImmutableDictionary TotalSupplies { get; } - - /// - public ValidatorSet? ValidatorSet { get; } - } - /// /// Almost duplicate https://github.com/planetarium/libplanet/blob/main/Libplanet/Action/ActionContext.cs. /// @@ -584,47 +120,54 @@ public LocalCacheBlockChainStates(IBlockChainStates source, string cacheDirector public IValue? GetState(Address address, BlockHash? offset) { - return GetBlockState(offset).GetState(address); + return GetAccountState(offset).GetState(address); } public IReadOnlyList GetStates(IReadOnlyList
addresses, BlockHash? offset) { - return GetBlockState(offset).GetStates(addresses); + return GetAccountState(offset).GetStates(addresses); } public FungibleAssetValue GetBalance(Address address, Currency currency, BlockHash? offset) { - return GetBlockState(offset).GetBalance(address, currency); + return GetAccountState(offset).GetBalance(address, currency); } public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) { - return GetBlockState(offset).GetTotalSupply(currency); + return GetAccountState(offset).GetTotalSupply(currency); } public ValidatorSet GetValidatorSet(BlockHash? offset) { - return GetBlockState(offset).GetValidatorSet(); + return GetAccountState(offset).GetValidatorSet(); } - public IBlockState GetBlockState(BlockHash? offset) + public IAccountState GetAccountState(BlockHash? offset) { - return new LocalCacheBlockState(_rocksDb, _source.GetBlockState(offset)); + return new LocalCacheAccountState(_rocksDb, _source.GetAccountState, offset); } } - private sealed class LocalCacheBlockState : IBlockState + private sealed class LocalCacheAccountState : IAccountState { private static readonly Codec _codec = new Codec(); private readonly RocksDb _rocksDb; - private readonly IBlockState _sourceBlockState; + private readonly Func _sourceAccountStateGetter; + private readonly BlockHash? _offset; - public LocalCacheBlockState(RocksDb rocksDb, IBlockState sourceBlockState) + public LocalCacheAccountState( + RocksDb rocksDb, + Func sourceAccountStateGetter, + BlockHash? offset) { _rocksDb = rocksDb; - _sourceBlockState = sourceBlockState; + _sourceAccountStateGetter = sourceAccountStateGetter; + _offset = offset; } + public ITrie Trie => _sourceAccountStateGetter(_offset).Trie; + public IValue? GetState(Address address) { var key = WithBlockHash(address.ToByteArray()); @@ -634,7 +177,7 @@ public LocalCacheBlockState(RocksDb rocksDb, IBlockState sourceBlockState) } catch (KeyNotFoundException) { - var state = _sourceBlockState.GetState(address); + var state = _sourceAccountStateGetter(_offset).GetState(address); SetValue(key, state); return state; } @@ -660,7 +203,7 @@ public FungibleAssetValue GetBalance(Address address, Currency currency) } catch (KeyNotFoundException) { - var fav = _sourceBlockState.GetBalance(address, currency); + var fav = _sourceAccountStateGetter(_offset).GetBalance(address, currency); SetValue(key, (Integer)fav.RawValue); return fav; } @@ -681,7 +224,7 @@ public FungibleAssetValue GetTotalSupply(Currency currency) } catch (KeyNotFoundException) { - var fav = _sourceBlockState.GetTotalSupply(currency); + var fav = _sourceAccountStateGetter(_offset).GetTotalSupply(currency); SetValue(key, (Integer)fav.RawValue); return fav; } @@ -697,14 +240,12 @@ public ValidatorSet GetValidatorSet() } catch (KeyNotFoundException) { - var validatorSet = _sourceBlockState.GetValidatorSet(); + var validatorSet = _sourceAccountStateGetter(_offset).GetValidatorSet(); SetValue(key, validatorSet.Bencoded); return validatorSet; } } - public BlockHash? BlockHash => _sourceBlockState.BlockHash; - private IValue? GetValue(byte[] key) { if (_rocksDb.Get(key) is not { } bytes) @@ -722,7 +263,7 @@ private void SetValue(byte[] key, IValue? value) private byte[] WithBlockHash(params byte[][] suffixes) { - if (BlockHash is not { } blockHash) + if (_offset is not { } blockHash) { throw new InvalidOperationException(); } diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs index 0f8f70f6e..19ebea278 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs @@ -100,8 +100,8 @@ public int Tx( } // Evaluate tx. - IAccountState previousBlockStates = blockChain.GetBlockState(previousBlock.Hash); - IAccount previousStates = AccountStateDelta.Create(previousBlockStates); + IAccountState previousBlockStates = blockChain.GetAccountState(previousBlock.Hash); + IAccount previousStates = new Account(previousBlockStates); var actions = tx.Actions.Select(a => ToAction(a)); var actionEvaluations = EvaluateActions( preEvaluationHash: targetBlock.PreEvaluationHash, @@ -398,8 +398,7 @@ public int RemoteTx( cacheDirectory ?? Path.Join(Path.GetTempPath(), "ncd-replay-remote-tx-cache")); var previousBlockHash = BlockHash.FromString(previousBlockHashValue); - var previousStates = - AccountStateDelta.Create(blockChainStates.GetBlockState(previousBlockHash)); + var previousStates = new Account(blockChainStates.GetAccountState(previousBlockHash)); var actions = transaction.Actions .Select(ToAction) diff --git a/NineChronicles.Headless.Tests/Common/MockState.cs b/NineChronicles.Headless.Tests/Common/MockState.cs index 8ad33c2d0..2ca86f1b0 100644 --- a/NineChronicles.Headless.Tests/Common/MockState.cs +++ b/NineChronicles.Headless.Tests/Common/MockState.cs @@ -6,6 +6,7 @@ using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Consensus; @@ -85,6 +86,8 @@ private MockState( public ValidatorSet ValidatorSet => _validatorSet; + public ITrie Trie => throw new NotSupportedException(); + public IValue? GetState(Address address) => _states.TryGetValue(address, out IValue? value) ? value : null; diff --git a/NineChronicles.Headless.Tests/GraphQLTestUtils.cs b/NineChronicles.Headless.Tests/GraphQLTestUtils.cs index 12b3ba9bb..ce2ad8c6b 100644 --- a/NineChronicles.Headless.Tests/GraphQLTestUtils.cs +++ b/NineChronicles.Headless.Tests/GraphQLTestUtils.cs @@ -92,7 +92,7 @@ public static StandaloneContext CreateStandaloneContext() stateStore, genesisBlock, actionEvaluator); - var currencyFactory = new CurrencyFactory(blockchain.GetBlockState); + var currencyFactory = new CurrencyFactory(() => blockchain.GetAccountState(blockchain.Tip.Hash)); var fungibleAssetValueFactory = new FungibleAssetValueFactory(currencyFactory); return new StandaloneContext { @@ -133,7 +133,7 @@ PrivateKey minerPrivateKey actionEvaluator); var ncg = new GoldCurrencyState((Dictionary)blockchain.GetState(Addresses.GoldCurrency)) .Currency; - var currencyFactory = new CurrencyFactory(blockchain.GetBlockState, ncg); + var currencyFactory = new CurrencyFactory(() => blockchain.GetAccountState(blockchain.Tip.Hash), ncg); var fungibleAssetValueFactory = new FungibleAssetValueFactory(currencyFactory); return new StandaloneContext { diff --git a/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs index 652d3c940..5716a9479 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs @@ -343,7 +343,7 @@ public async Task TransactionResultIsSuccess() private Task ExecuteAsync(string query) { - var currencyFactory = new CurrencyFactory(_blockChain.GetBlockState); + var currencyFactory = new CurrencyFactory(() => _blockChain.GetAccountState(_blockChain.Tip.Hash)); var fungibleAssetValueFactory = new FungibleAssetValueFactory(currencyFactory); return GraphQLTestUtils.ExecuteQueryAsync(query, standaloneContext: new StandaloneContext { diff --git a/NineChronicles.Headless/BlockChainService.cs b/NineChronicles.Headless/BlockChainService.cs index 050924f90..6f6ac1117 100644 --- a/NineChronicles.Headless/BlockChainService.cs +++ b/NineChronicles.Headless/BlockChainService.cs @@ -125,7 +125,7 @@ public UnaryResult GetState(byte[] addressBytes, byte[] blockHashBytes) public async UnaryResult> GetAvatarStates(IEnumerable addressBytesList, byte[] blockHashBytes) { var hash = new BlockHash(blockHashBytes); - var accountState = _blockChain.GetBlockState(hash); + var accountState = _blockChain.GetAccountState(hash); var result = new ConcurrentDictionary(); var addresses = addressBytesList.Select(a => new Address(a)).ToList(); var rawAvatarStates = accountState.GetRawAvatarStates(addresses); diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs index ee5648bbf..a5d9688d5 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs @@ -52,7 +52,7 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi } return new StateContext( - chain.GetBlockState(blockHash), + chain.GetAccountState(blockHash), blockHash switch { BlockHash bh => chain[bh].Index, diff --git a/NineChronicles.Headless/NineChroniclesNodeService.cs b/NineChronicles.Headless/NineChroniclesNodeService.cs index 5b2f22d65..155068c0e 100644 --- a/NineChronicles.Headless/NineChroniclesNodeService.cs +++ b/NineChronicles.Headless/NineChroniclesNodeService.cs @@ -278,7 +278,8 @@ internal void ConfigureContext(StandaloneContext standaloneContext) standaloneContext.Store = Store; standaloneContext.Swarm = Swarm; standaloneContext.CurrencyFactory = - new CurrencyFactory(standaloneContext.BlockChain.GetBlockState); + new CurrencyFactory( + () => standaloneContext.BlockChain.GetAccountState(standaloneContext.BlockChain.Tip.Hash)); standaloneContext.FungibleAssetValueFactory = new FungibleAssetValueFactory(standaloneContext.CurrencyFactory); BootstrapEnded.WaitAsync().ContinueWith((task) => From 47cf9830e3f115a0dc8d9e7d4307f10228945628 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Mon, 11 Sep 2023 19:15:17 +0900 Subject: [PATCH 29/35] bump lib9c:release/1.1.0 --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 6a5fd2f48..4a354e5a8 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 6a5fd2f48852d26dcd6b168c294813f30575da7a +Subproject commit 4a354e5a8cfc21535bf4e52347e442e8cbfc131d From a38bc8a071d83d49dd276313afc3a9371bcd53e3 Mon Sep 17 00:00:00 2001 From: moreal Date: Tue, 12 Sep 2023 13:52:03 +0900 Subject: [PATCH 30/35] Support regex for `transaction.ncTransactions`' actionType argument --- .../GraphTypes/TransactionHeadlessQuery.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs b/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs index ecaa294ee..5be72bb01 100644 --- a/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs +++ b/NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; +using System.Text.RegularExpressions; using Bencodex; using Bencodex.Json; using GraphQL; @@ -75,7 +76,7 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext) new QueryArgument> { Name = "limit", Description = "number of block to query." }, new QueryArgument> - { Name = "actionType", Description = "filter tx by having actions' type" } + { Name = "actionType", Description = "filter tx by having actions' type. It is regular expression." } ), resolve: context => { @@ -99,7 +100,7 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext) return false; } - return typeId == actionType; + return Regex.IsMatch(typeId, actionType); })); return transactions; From 9bf3baad29d6f29cbc33184f4bccc1f9ca2af0ec Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Sep 2023 17:18:05 +0900 Subject: [PATCH 31/35] Bump lib9c: Apply summon 10+1 --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 4a354e5a8..4f991b69a 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 4a354e5a8cfc21535bf4e52347e442e8cbfc131d +Subproject commit 4f991b69abc4d0b0f889f228115890ee7c4ea1f7 From 569ed8eef7f0e2b063125e7feeff42872abc8cfe Mon Sep 17 00:00:00 2001 From: moreal Date: Wed, 13 Sep 2023 11:29:16 +0900 Subject: [PATCH 32/35] Bump lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 4f991b69a..6bbeac6fe 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 4f991b69abc4d0b0f889f228115890ee7c4ea1f7 +Subproject commit 6bbeac6fe071727019d61732887be4cb91e4ce7a From 1109af354b790d8099101d3ba26896cb18479a07 Mon Sep 17 00:00:00 2001 From: Hyun Seungmin Date: Mon, 18 Sep 2023 16:36:10 +0900 Subject: [PATCH 33/35] Bump lib9c:release/1.1.0 head --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index 6bbeac6fe..a197d899e 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit 6bbeac6fe071727019d61732887be4cb91e4ce7a +Subproject commit a197d899e9764d0d381239f08bedcd148ea30cd0 From 5041f5a2ecfce37725c0eaa8552cde4463179e7c Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 19 Sep 2023 15:13:20 +0900 Subject: [PATCH 34/35] Bump lib9c --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index a197d899e..aa9018cdc 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit a197d899e9764d0d381239f08bedcd148ea30cd0 +Subproject commit aa9018cdc659a51cc7781b2072345dac6a1eac23 From 368b4349cb6504f0b787c79c0699a5c363dd5612 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 19 Sep 2023 16:01:07 +0900 Subject: [PATCH 35/35] Bump lib9c main --- Lib9c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c b/Lib9c index aa9018cdc..fe86a78e2 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit aa9018cdc659a51cc7781b2072345dac6a1eac23 +Subproject commit fe86a78e29d7d15486779b133bea21ba0ec63f30