diff --git a/Lib9c b/Lib9c index cb19e751c..7575c9a65 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit cb19e751cef50a701af2d91d43fb62671265cc4b +Subproject commit 7575c9a657bcc3a43360de823568ee3b9f0219c9 diff --git a/Libplanet.Extensions.ForkableActionEvaluator.Tests/ForkableActionEvaluatorTest.cs b/Libplanet.Extensions.ForkableActionEvaluator.Tests/ForkableActionEvaluatorTest.cs index 73ea081fa..fe68994d0 100644 --- a/Libplanet.Extensions.ForkableActionEvaluator.Tests/ForkableActionEvaluatorTest.cs +++ b/Libplanet.Extensions.ForkableActionEvaluator.Tests/ForkableActionEvaluatorTest.cs @@ -62,7 +62,8 @@ public void CheckPairs() class PostActionEvaluator : IActionEvaluator { public IActionLoader ActionLoader => throw new NotSupportedException(); - public IReadOnlyList Evaluate(IPreEvaluationBlock block, HashDigest? baseStateroothash) + public IReadOnlyList Evaluate( + IPreEvaluationBlock block, HashDigest? baseStateRootHash) { return new ICommittedActionEvaluation[] { @@ -85,7 +86,8 @@ public IReadOnlyList Evaluate(IPreEvaluationBlock bl class PreActionEvaluator : IActionEvaluator { public IActionLoader ActionLoader => throw new NotSupportedException(); - public IReadOnlyList Evaluate(IPreEvaluationBlock block, HashDigest? baseStateRootHash) + public IReadOnlyList Evaluate( + IPreEvaluationBlock block, HashDigest? baseStateRootHash) { return new ICommittedActionEvaluation[] { @@ -113,7 +115,7 @@ public void LoadPlainValue(IValue plainValue) { } - public IAccount Execute(IActionContext context) => context.PreviousState; + public IWorld Execute(IActionContext context) => context.PreviousState; } class MockBlock : IPreEvaluationBlock diff --git a/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs b/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs index e97732f96..9c70a1cb8 100644 --- a/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs +++ b/Libplanet.Extensions.PluggedActionEvaluator/PluggedActionEvaluator.cs @@ -46,10 +46,15 @@ public static IPluginActionEvaluator CreateActionEvaluator(Assembly assembly, st public static IPluginActionEvaluator CreateActionEvaluator(string pluginPath, string typeName, IKeyValueStore keyValueStore) => CreateActionEvaluator(LoadPlugin(pluginPath), typeName, new PluginKeyValueStore(keyValueStore)); - public IReadOnlyList Evaluate(IPreEvaluationBlock block, HashDigest? baseStateRootHash) - => _pluginActionEvaluator.Evaluate( - PreEvaluationBlockMarshaller.Serialize(block), - baseStateRootHash is { } srh ? srh.ToByteArray() : null) + public IReadOnlyList Evaluate( + IPreEvaluationBlock block, + HashDigest? baseStateRootHash) + { + var evaluations = _pluginActionEvaluator.Evaluate( + PreEvaluationBlockMarshaller.Serialize(block), + baseStateRootHash is { } srh ? srh.ToByteArray() : null) .Select(eval => ActionEvaluationMarshaller.Deserialize(eval)).ToList().AsReadOnly(); + return evaluations; + } } } diff --git a/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs b/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs index 7ccd5cd94..6be8af511 100644 --- a/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs +++ b/Libplanet.Headless.Tests/Hosting/LibplanetNodeServiceTest.cs @@ -89,7 +89,7 @@ private class DummyAction : IAction { IValue IAction.PlainValue => Dictionary.Empty; - IAccount IAction.Execute(IActionContext context) + IWorld IAction.Execute(IActionContext context) { return context.PreviousState; } diff --git a/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs index c4502e5f1..29eade8fb 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/ActionCommandTest.cs @@ -5,7 +5,6 @@ using Libplanet.Common; using Libplanet.Crypto; using Nekoyume.Action; -using Nekoyume.Action.Factory; using Nekoyume.Model; using Nekoyume.Model.State; using NineChronicles.Headless.Executable.Commands; @@ -59,7 +58,6 @@ public void ActivateAccount(bool invalid, int expectedCode) } } - [Theory] [InlineData(10, 0, "transfer asset test1.")] [InlineData(100, 0, "transfer asset test2.")] @@ -142,79 +140,6 @@ public void ClaimStakeReward(string addressString, int expectedCode) } } - [Theory] - [InlineData(0L, typeof(ClaimStakeReward2))] - [InlineData(ClaimStakeReward2.ObsoletedIndex, typeof(ClaimStakeReward2))] - [InlineData(ClaimStakeReward2.ObsoletedIndex + 1, typeof(ClaimStakeReward3))] - [InlineData(ClaimStakeReward3.ObsoleteBlockIndex, typeof(ClaimStakeReward3))] - [InlineData(ClaimStakeReward3.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward4))] - [InlineData(ClaimStakeReward4.ObsoleteBlockIndex, typeof(ClaimStakeReward4))] - [InlineData(ClaimStakeReward4.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward5))] - [InlineData(ClaimStakeReward5.ObsoleteBlockIndex, typeof(ClaimStakeReward5))] - [InlineData(ClaimStakeReward5.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward6))] - [InlineData(ClaimStakeReward6.ObsoleteBlockIndex, typeof(ClaimStakeReward6))] - [InlineData(ClaimStakeReward6.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward7))] - [InlineData(ClaimStakeReward7.ObsoleteBlockIndex, typeof(ClaimStakeReward7))] - [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) - { - var filePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); - var addr = new PrivateKey().Address; - var resultCode = _command.ClaimStakeReward( - addr.ToHex(), - filePath, - blockIndex: blockIndex); - Assert.Equal(0, resultCode); - - var rawAction = Convert.FromBase64String(File.ReadAllText(filePath)); - var decoded = (List)_codec.Decode(rawAction); - var plainValue = Assert.IsType(decoded[1]); - var action = ClaimStakeRewardFactory.CreateByBlockIndex(blockIndex, addr); - Assert.NotNull(action); - var actionType = action.GetType(); - Assert.Equal(expectedActionType, actionType); - action.LoadPlainValue(plainValue); - string type = (Text)decoded[0]; - Assert.Equal(type, actionType.Name); - } - - [Theory] - [InlineData(0, 0, -1)] - [InlineData(1, 9, 0)] - [InlineData(10, 10, -1)] - public void ClaimStakeRewardWithActionVersion( - int actionVersionMin, - int actionVersionMax, - int expectedCode) - { - for (var i = actionVersionMin; i < actionVersionMax + 1; i++) - { - var filePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); - var addr = new PrivateKey().Address; - var resultCode = _command.ClaimStakeReward( - addr.ToHex(), - filePath, - actionVersion: i); - Assert.Equal(expectedCode, resultCode); - - if (expectedCode < 0) - { - continue; - } - - var rawAction = Convert.FromBase64String(File.ReadAllText(filePath)); - var decoded = (List)_codec.Decode(rawAction); - var plainValue = Assert.IsType(decoded[1]); - var action = ClaimStakeRewardFactory.CreateByVersion(i, addr); - action.LoadPlainValue(plainValue); - string type = (Text)decoded[0]; - Assert.Equal(action.GetType().Name, type); - } - } - [Theory] [InlineData("0xab1dce17dCE1Db1424BB833Af6cC087cd4F5CB6d", -1)] [InlineData("ab1dce17dCE1Db1424BB833Af6cC087cd4F5CB6d", 0)] diff --git a/NineChronicles.Headless.Executable.Tests/Commands/TxCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/TxCommandTest.cs index 5a970cbee..1704478f8 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/TxCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/TxCommandTest.cs @@ -78,7 +78,6 @@ public void Sign_Stake(bool gas) [InlineData(ClaimStakeReward2.ObsoletedIndex - 1, null, false)] [InlineData(ClaimStakeReward2.ObsoletedIndex, null, true)] [InlineData(ClaimStakeReward2.ObsoletedIndex + 1, null, false)] - [InlineData(ClaimStakeReward3.ObsoleteBlockIndex - 1, null, true)] [InlineData(long.MaxValue, null, true)] [InlineData(null, 1, false)] [InlineData(null, 2, true)] @@ -92,9 +91,7 @@ public void Sign_ClaimStakeReward(long? blockIndex, int? actionVersion, bool gas var avatarAddress = new Address(); actionCommand.ClaimStakeReward( avatarAddress.ToHex(), - filePath, - blockIndex, - actionVersion); + filePath); Assert_Tx(1, filePath, gas); } diff --git a/NineChronicles.Headless.Executable/Commands/AccountCommand.cs b/NineChronicles.Headless.Executable/Commands/AccountCommand.cs index 1b74370fe..9531c62f4 100644 --- a/NineChronicles.Headless.Executable/Commands/AccountCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/AccountCommand.cs @@ -3,6 +3,7 @@ using System.Linq; using Bencodex; using Cocona; +using Libplanet.Action.State; using Libplanet.Blockchain; using Libplanet.Crypto; using Libplanet.Store; @@ -11,6 +12,7 @@ using Libplanet.Types.Tx; using Nekoyume.Action; using Nekoyume.Model.State; +using Nekoyume.Module; using NineChronicles.Headless.Executable.IO; using Serilog.Core; using static NineChronicles.Headless.NCActionUtils; @@ -52,15 +54,16 @@ public void Balance( Block offset = DevExUtils.ParseBlockOffset(chain, block); _console.Error.WriteLine("The offset block: #{0} {1}.", offset.Index, offset.Hash); + IWorldState worldState = chain.GetWorldState(offset.Hash); Bencodex.Types.Dictionary goldCurrencyStateDict = (Bencodex.Types.Dictionary) - chain.GetState(GoldCurrencyState.Address); + worldState.GetLegacyState(GoldCurrencyState.Address); GoldCurrencyState goldCurrencyState = new GoldCurrencyState(goldCurrencyStateDict); Currency gold = goldCurrencyState.Currency; if (address is { } addrStr) { Address addr = DevExUtils.ParseAddress(addrStr); - FungibleAssetValue balance = chain.GetBalance(addr, gold, offset.Hash); + FungibleAssetValue balance = worldState.GetBalance(addr, gold); _console.Out.WriteLine("{0}\t{1}", addr, balance); return; } @@ -96,7 +99,7 @@ public void Balance( { if (!printed.Contains(addr)) { - FungibleAssetValue balance = chain.GetBalance(addr, gold, offset.Hash); + FungibleAssetValue balance = worldState.GetBalance(addr, gold); _console.Out.WriteLine("{0}\t{1}", addr, balance); printed.Add(addr); } diff --git a/NineChronicles.Headless.Executable/Commands/ActionCommand.cs b/NineChronicles.Headless.Executable/Commands/ActionCommand.cs index eeea168db..1ddabd47c 100644 --- a/NineChronicles.Headless.Executable/Commands/ActionCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ActionCommand.cs @@ -12,7 +12,6 @@ using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume.Action; -using Nekoyume.Action.Factory; using Nekoyume.Model; using NineChronicles.Headless.Executable.IO; @@ -220,44 +219,14 @@ public int ClaimStakeReward( [Argument("AVATAR-ADDRESS", Description = "A hex-encoded avatar address.")] string encodedAddress, [Argument("PATH", Description = "A file path of base64 encoded action.")] - string? filePath = null, - [Option("BLOCK-INDEX", Description = "A block index which is used to specifying the action version.")] - long? blockIndex = null, - [Option("ACTION-VERSION", Description = "A version of action.")] - int? actionVersion = null + string? filePath = null ) { try { - if (blockIndex.HasValue && actionVersion.HasValue) - { - throw new CommandExitedException( - "You can't specify both block index and action version at the same time.", - -1); - } - Address avatarAddress = new Address(ByteUtil.ParseHex(encodedAddress)); IClaimStakeReward? action = null; - if (blockIndex.HasValue) - { - action = ClaimStakeRewardFactory.CreateByBlockIndex( - blockIndex.Value, - avatarAddress); - } - else if (actionVersion.HasValue) - { - action = ClaimStakeRewardFactory.CreateByVersion( - actionVersion.Value, - avatarAddress); - } - - // NOTE: If neither block index nor action version is specified, - // it will be created by the type of the class. - // I considered to create action with max value of - // block index(i.e., long.MaxValue), but it is not good - // because the action of the next version may come along - // with the current version. - action ??= new ClaimStakeReward(avatarAddress); + action = new ClaimStakeReward(avatarAddress); byte[] raw = Codec.Encode(new List( new[] diff --git a/NineChronicles.Headless.Executable/Commands/MarketCommand.cs b/NineChronicles.Headless.Executable/Commands/MarketCommand.cs index 21fd70b8c..137ef4380 100644 --- a/NineChronicles.Headless.Executable/Commands/MarketCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/MarketCommand.cs @@ -14,6 +14,7 @@ using Libplanet.Types.Tx; using Nekoyume.Action; using Nekoyume.Model.Item; +using Nekoyume.Module; using NineChronicles.Headless.Executable.IO; using Serilog.Core; using static NineChronicles.Headless.NCActionUtils; @@ -130,7 +131,7 @@ public void Query( { int? quantity = null; if (p.OrderId is { } oid && - chain.GetState(GetOrderAddress(oid)) is Dictionary rawOrder) + chain.GetWorldState().GetLegacyState(GetOrderAddress(oid)) is Dictionary rawOrder) { if (OrderFactory.Deserialize(rawOrder) is FungibleOrder fo) { diff --git a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs index ee3c1ff4b..2a5154b23 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.Privates.cs @@ -2,23 +2,14 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.IO; -using System.Linq; using System.Security.Cryptography; -using Bencodex; -using Bencodex.Types; using Cocona; using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Action; using Libplanet.Action.State; -using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; -using Libplanet.Types.Blocks; using Libplanet.Types.Tx; -using RocksDbSharp; using Serilog; -using Libplanet.Store.Trie; namespace NineChronicles.Headless.Executable.Commands { @@ -35,7 +26,7 @@ public ActionContext( Address miner, long blockIndex, int blockProtocolVersion, - IAccount previousState, + IWorld previousState, int randomSeed, bool rehearsal = false) { @@ -61,7 +52,7 @@ public ActionContext( public bool Rehearsal { get; } - public IAccount PreviousState { get; } + public IWorld PreviousState { get; } public int RandomSeed { get; } @@ -89,185 +80,6 @@ public Random(int seed) public int Seed { get; private set; } } - private sealed class LocalCacheBlockChainStates : IBlockChainStates - { - private readonly IBlockChainStates _source; - private readonly RocksDb _rocksDb; - - public LocalCacheBlockChainStates(IBlockChainStates source, string cacheDirectory) - { - _source = source; - var options = new DbOptions().SetCreateIfMissing(); - _rocksDb = RocksDb.Open(options, cacheDirectory); - } - - public IValue? GetState(Address address, BlockHash? offset) - { - return GetAccountState(offset).GetState(address); - } - - public IReadOnlyList GetStates(IReadOnlyList
addresses, BlockHash? offset) - { - return GetAccountState(offset).GetStates(addresses); - } - - public FungibleAssetValue GetBalance(Address address, Currency currency, BlockHash? offset) - { - return GetAccountState(offset).GetBalance(address, currency); - } - - public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) - { - return GetAccountState(offset).GetTotalSupply(currency); - } - - public ValidatorSet GetValidatorSet(BlockHash? offset) - { - return GetAccountState(offset).GetValidatorSet(); - } - - public IAccountState GetAccountState(BlockHash? offset) - { - return new LocalCacheAccountState( - _rocksDb, - _source.GetAccountState, - offset); - } - - public IAccountState GetAccountState(HashDigest? hash) - => _source.GetAccountState(hash); - } - - private sealed class LocalCacheAccountState : IAccountState - { - private static readonly Codec _codec = new Codec(); - private readonly RocksDb _rocksDb; - private readonly Func _sourceAccountStateGetter; - private readonly BlockHash? _offset; - - public LocalCacheAccountState( - RocksDb rocksDb, - Func sourceAccountStateGetterWithBlockHash, - BlockHash? offset) - { - _rocksDb = rocksDb; - _sourceAccountStateGetter = sourceAccountStateGetterWithBlockHash; - _offset = offset; - } - - public ITrie Trie => _sourceAccountStateGetter(_offset).Trie; - - public IValue? GetState(Address address) - { - var key = WithBlockHash(address.ToByteArray()); - try - { - return GetValue(key); - } - catch (KeyNotFoundException) - { - var state = _sourceAccountStateGetter(_offset).GetState(address); - SetValue(key, state); - return state; - } - } - - public IReadOnlyList GetStates(IReadOnlyList
addresses) - { - return addresses.Select(GetState).ToList(); - } - - public FungibleAssetValue GetBalance(Address address, Currency currency) - { - var key = WithBlockHash(address.ToByteArray(), currency.Hash.ToByteArray()); - try - { - var state = GetValue(key); - if (state is not Integer integer) - { - throw new InvalidOperationException(); - } - - return FungibleAssetValue.FromRawValue(currency, integer); - } - catch (KeyNotFoundException) - { - var fav = _sourceAccountStateGetter(_offset).GetBalance(address, currency); - SetValue(key, (Integer)fav.RawValue); - return fav; - } - } - - public FungibleAssetValue GetTotalSupply(Currency currency) - { - var key = WithBlockHash(currency.Hash.ToByteArray()); - try - { - var state = GetValue(key); - if (state is not Integer integer) - { - throw new InvalidOperationException(); - } - - return FungibleAssetValue.FromRawValue(currency, integer); - } - catch (KeyNotFoundException) - { - var fav = _sourceAccountStateGetter(_offset).GetTotalSupply(currency); - SetValue(key, (Integer)fav.RawValue); - return fav; - } - } - - public ValidatorSet GetValidatorSet() - { - var key = WithBlockHash(new byte[] { 0x5f, 0x5f, 0x5f }); - try - { - var state = GetValue(key); - return state is not null ? new ValidatorSet(state) : new ValidatorSet(); - } - catch (KeyNotFoundException) - { - var validatorSet = _sourceAccountStateGetter(_offset).GetValidatorSet(); - SetValue(key, validatorSet.Bencoded); - return validatorSet; - } - } - - private IValue? GetValue(byte[] key) - { - if (_rocksDb.Get(key) is not { } bytes) - { - throw new KeyNotFoundException(); - } - - return bytes[0] == 'x' ? null : _codec.Decode(bytes); - } - - private void SetValue(byte[] key, IValue? value) - { - _rocksDb.Put(key, value is null ? new byte[] { 0x78 } : _codec.Encode(value)); - } - - private byte[] WithBlockHash(params byte[][] suffixes) - { - if (_offset is not { } blockHash) - { - throw new InvalidOperationException(); - } - - var stream = new MemoryStream(Libplanet.Types.Blocks.BlockHash.Size + suffixes.Sum(s => s.Length)); - stream.Write(blockHash.ToByteArray()); - foreach (var suffix in suffixes) - { - stream.Write(suffix); - } - - return stream.ToArray(); - } - } - /// /// Almost duplicate https://github.com/planetarium/libplanet/blob/main/Libplanet/Action/ActionEvaluator.cs#L286. /// @@ -276,7 +88,7 @@ private static IEnumerable EvaluateActions( long blockIndex, int blockProtocolVersion, TxId? txid, - IAccount previousStates, + IWorld previousStates, Address miner, Address signer, byte[] signature, @@ -284,7 +96,7 @@ private static IEnumerable EvaluateActions( ILogger? logger = null) { ActionContext CreateActionContext( - IAccount prevState, + IWorld prevState, int randomSeed) { return new ActionContext( @@ -300,11 +112,11 @@ ActionContext CreateActionContext( byte[] preEvaluationHashBytes = preEvaluationHash.ToByteArray(); int seed = ActionEvaluator.GenerateRandomSeed(preEvaluationHashBytes, signature, 0); - IAccount states = previousStates; + IWorld states = previousStates; foreach (IAction action in actions) { Exception? exc = null; - IAccount nextStates = states; + IWorld 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 dd06de842..9e1db0b20 100644 --- a/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ReplayCommand.cs @@ -14,7 +14,6 @@ using Cocona.Help; using GraphQL.Client.Http; using GraphQL.Client.Serializer.SystemTextJson; -using Libplanet; using Libplanet.Action; using Libplanet.Action.Loader; using Libplanet.Blockchain; @@ -32,7 +31,6 @@ using Nekoyume.Blockchain.Policy; using NineChronicles.Headless.Executable.IO; using NineChronicles.Headless.Executable.Store; -using Serilog.Core; using static NineChronicles.Headless.NCActionUtils; namespace NineChronicles.Headless.Executable.Commands @@ -100,15 +98,14 @@ public int Tx( } // Evaluate tx. - IAccountState previousBlockStates = blockChain.GetAccountState(previousBlock.Hash); - IAccount previousStates = new Account(previousBlockStates); + IWorld previousWorld = new World(blockChain.GetWorldState(previousBlock.Hash)); var actions = tx.Actions.Select(a => ToAction(a)); var actionEvaluations = EvaluateActions( preEvaluationHash: targetBlock.PreEvaluationHash, blockIndex: targetBlock.Index, blockProtocolVersion: targetBlock.ProtocolVersion, txid: tx.Id, - previousStates: previousStates, + previousStates: previousWorld, miner: targetBlock.Miner, signer: tx.Signer, signature: tx.Signature, @@ -357,12 +354,7 @@ public int RemoteTx( [Option("tx", new[] { 't' }, Description = "The transaction id")] string transactionId, [Option("endpoint", new[] { 'e' }, Description = "GraphQL endpoint to get remote state")] - string endpoint, - [Option( - "cache-directory", - new [] { 'c' }, - Description = "A directory to store states, balances, etc as cache")] - string? cacheDirectory=null) + string endpoint) { var graphQlClient = new GraphQLHttpClient(new Uri(endpoint), new SystemTextJsonSerializer()); var transactionResponse = GetTransactionData(graphQlClient, transactionId); @@ -395,12 +387,10 @@ public int RemoteTx( var miner = new Address(minerValue); var explorerEndpoint = $"{endpoint}/explorer"; - var blockChainStates = new LocalCacheBlockChainStates( - new RemoteBlockChainStates(new Uri(explorerEndpoint)), - cacheDirectory ?? Path.Join(Path.GetTempPath(), "ncd-replay-remote-tx-cache")); + var blockChainStates = new RemoteBlockChainStates(new Uri(explorerEndpoint)); var previousBlockHash = BlockHash.FromString(previousBlockHashValue); - var previousStates = new Account(blockChainStates.GetAccountState(previousBlockHash)); + var previousStates = new World(blockChainStates.GetWorldState(previousBlockHash)); var actions = transaction.Actions .Select(ToAction) diff --git a/NineChronicles.Headless.Executable/Commands/TxCommand.cs b/NineChronicles.Headless.Executable/Commands/TxCommand.cs index 0e77fd33b..823073764 100644 --- a/NineChronicles.Headless.Executable/Commands/TxCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/TxCommand.cs @@ -67,14 +67,6 @@ public void Sign( { nameof(ActivateAccount) => new ActivateAccount(), nameof(Stake) => new Stake(), - // FIXME: This `ClaimStakeReward` cases need to reduce to one case. - nameof(ClaimStakeReward1) => new ClaimStakeReward1(), - nameof(ClaimStakeReward2) => new ClaimStakeReward2(), - nameof(ClaimStakeReward3) => new ClaimStakeReward3(), - 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(), @@ -185,20 +177,6 @@ string tablePath _console.Out.WriteLine(ByteUtil.Hex(raw)); } - [Command(Description = "Create MigrationLegacyShop action and dump it.")] - public void MigrationLegacyShop() - { - var action = new MigrationLegacyShop(); - - var bencoded = new List( - (Text)nameof(Nekoyume.Action.MigrationLegacyShop), - action.PlainValue - ); - - byte[] raw = _codec.Encode(bencoded); - _console.Out.WriteLine(ByteUtil.Hex(raw)); - } - [Command(Description = "Create MigrationActivatedAccountsState action and dump it.")] public void MigrationActivatedAccountsState() { diff --git a/NineChronicles.Headless.Tests/AccountStateExtensionTest.cs b/NineChronicles.Headless.Tests/AccountStateExtensionTest.cs deleted file mode 100644 index 0ee93b1c1..000000000 --- a/NineChronicles.Headless.Tests/AccountStateExtensionTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Nekoyume.Action; -using NineChronicles.Headless.Tests.Common; -using Xunit; -using static Lib9c.SerializeKeys; - -namespace NineChronicles.Headless.Tests -{ - public class AccountStateExtensionTest - { - [Theory] - [InlineData(true, false, false, false, false)] - [InlineData(true, false, false, false, true)] - [InlineData(false, true, true, true, false)] - [InlineData(false, false, true, true, true)] - [InlineData(false, true, false, true, true)] - [InlineData(false, true, true, false, true)] - public void GetAvatarState(bool backward, bool inventoryExist, bool worldInformationExist, bool questListExist, bool exc) - { - MockState mockState = MockState.Empty; - - mockState = backward - ? mockState.SetState(Fixtures.AvatarAddress, Fixtures.AvatarStateFX.Serialize()) - : mockState.SetState(Fixtures.AvatarAddress, Fixtures.AvatarStateFX.SerializeV2()); - mockState = inventoryExist - ? mockState.SetState( - Fixtures.AvatarAddress.Derive(LegacyInventoryKey), - Fixtures.AvatarStateFX.inventory.Serialize()) - : mockState; - mockState = worldInformationExist - ? mockState.SetState( - Fixtures.AvatarAddress.Derive(LegacyWorldInformationKey), - Fixtures.AvatarStateFX.worldInformation.Serialize()) - : mockState; - mockState = questListExist - ? mockState.SetState( - Fixtures.AvatarAddress.Derive(LegacyQuestListKey), - Fixtures.AvatarStateFX.questList.Serialize()) - : mockState; - - if (exc) - { - Assert.Throws(() => mockState.GetAvatarState(default)); - } - else - { - var avatarState = mockState.GetAvatarState(Fixtures.AvatarAddress); - - Assert.NotNull(avatarState.inventory); - Assert.NotNull(avatarState.worldInformation); - Assert.NotNull(avatarState.questList); - } - } - } -} diff --git a/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs b/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs index a33c0ecb2..eda0bcf80 100644 --- a/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs +++ b/NineChronicles.Headless.Tests/Common/Actions/EmptyAction.cs @@ -11,7 +11,7 @@ public void LoadPlainValue(IValue plainValue) { } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { return context.PreviousState; } diff --git a/NineChronicles.Headless.Tests/Common/KeyConverters.cs b/NineChronicles.Headless.Tests/Common/KeyConverters.cs new file mode 100644 index 000000000..43a64fd4f --- /dev/null +++ b/NineChronicles.Headless.Tests/Common/KeyConverters.cs @@ -0,0 +1,101 @@ +using Libplanet.Crypto; +using Libplanet.Store.Trie; +using Libplanet.Types.Assets; + +namespace NineChronicles.Headless.Tests.Common +{ + + /// + /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/KeyConverters.cs + /// except this has its constructors exposed as public for testing. + /// + internal static class KeyConverters + { + // "___" + public static readonly KeyBytes ValidatorSetKey = + new KeyBytes(new byte[] { _underScore, _underScore, _underScore }); + + private const byte _underScore = 95; // '_' + + private static readonly byte[] _conversionTable = + { + 48, // '0' + 49, // '1' + 50, // '2' + 51, // '3' + 52, // '4' + 53, // '5' + 54, // '6' + 55, // '7' + 56, // '8' + 57, // '9' + 97, // 'a' + 98, // 'b' + 99, // 'c' + 100, // 'd' + 101, // 'e' + 102, // 'f' + }; + + // $"{ByteUtil.Hex(address.ByteArray)}" + public static KeyBytes ToStateKey(Address address) + { + var addressBytes = address.ByteArray; + byte[] buffer = new byte[addressBytes.Length * 2]; + for (int i = 0; i < addressBytes.Length; i++) + { + buffer[i * 2] = _conversionTable[addressBytes[i] >> 4]; + buffer[i * 2 + 1] = _conversionTable[addressBytes[i] & 0xf]; + } + + return new KeyBytes(buffer); + } + + // $"_{ByteUtil.Hex(address.ByteArray)}_{ByteUtil.Hex(currency.Hash.ByteArray)}" + public static KeyBytes ToFungibleAssetKey(Address address, Currency currency) + { + var addressBytes = address.ByteArray; + var currencyBytes = currency.Hash.ByteArray; + byte[] buffer = new byte[addressBytes.Length * 2 + currencyBytes.Length * 2 + 2]; + + buffer[0] = _underScore; + for (int i = 0; i < addressBytes.Length; i++) + { + buffer[1 + i * 2] = _conversionTable[addressBytes[i] >> 4]; + buffer[1 + i * 2 + 1] = _conversionTable[addressBytes[i] & 0xf]; + } + + var offset = addressBytes.Length * 2; + buffer[offset + 1] = _underScore; + for (int i = 0; i < currencyBytes.Length; i++) + { + buffer[offset + 2 + i * 2] = _conversionTable[currencyBytes[i] >> 4]; + buffer[offset + 2 + i * 2 + 1] = _conversionTable[currencyBytes[i] & 0xf]; + } + + return new KeyBytes(buffer); + } + + public static KeyBytes ToFungibleAssetKey( + (Address Address, Currency Currency) pair) => + ToFungibleAssetKey(pair.Address, pair.Currency); + + // $"__{ByteUtil.Hex(currency.Hash.ByteArray)}" + public static KeyBytes ToTotalSupplyKey(Currency currency) + { + var currencyBytes = currency.Hash.ByteArray; + byte[] buffer = new byte[currencyBytes.Length * 2 + 2]; + + buffer[0] = _underScore; + buffer[1] = _underScore; + + for (int i = 0; i < currencyBytes.Length; i++) + { + buffer[2 + i * 2] = _conversionTable[currencyBytes[i] >> 4]; + buffer[2 + i * 2 + 1] = _conversionTable[currencyBytes[i] & 0xf]; + } + + return new KeyBytes(buffer); + } + } +} diff --git a/NineChronicles.Headless.Tests/Common/MockAccount.cs b/NineChronicles.Headless.Tests/Common/MockAccount.cs new file mode 100644 index 000000000..ac2d3acdd --- /dev/null +++ b/NineChronicles.Headless.Tests/Common/MockAccount.cs @@ -0,0 +1,295 @@ +namespace NineChronicles.Headless.Tests.Common +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Diagnostics.Contracts; + using System.Numerics; + using Bencodex.Types; + using Libplanet.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Store.Trie; + using Libplanet.Types.Assets; + using Libplanet.Types.Consensus; + using static KeyConverters; + + /// + /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/Account.cs + /// except this has its constructors exposed as public for testing. + /// + [Pure] + public class MockAccount : IAccount + { + private readonly MockAccountState _state; + + public MockAccount(MockAccountState state) + : this(state, ImmutableHashSet<(Address, Currency)>.Empty) + { + } + + public MockAccount( + MockAccountState state, + IImmutableSet<(Address, Currency)> totalUpdatedFungibleAssets) + { + _state = state; + TotalUpdatedFungibleAssets = totalUpdatedFungibleAssets; + } + + /// + public ITrie Trie => throw new NotSupportedException(); + + public IImmutableDictionary MockTrie => _state.MockTrie; + + /// + public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets { get; } + + /// + [Pure] + public IValue? GetState(Address address) => _state.GetState(address); + + /// + [Pure] + public IReadOnlyList GetStates(IReadOnlyList
addresses) => + _state.GetStates(addresses); + + /// + [Pure] + public IAccount SetState(Address address, IValue state) => UpdateState(address, state); + + /// + [Pure] + public IAccount RemoveState(Address address) => UpdateState(address); + + /// + [Pure] + public FungibleAssetValue GetBalance(Address address, Currency currency) => + _state.GetBalance(address, currency); + + /// + [Pure] + public FungibleAssetValue GetTotalSupply(Currency currency) => + _state.GetTotalSupply(currency); + + /// + [Pure] + public ValidatorSet GetValidatorSet() => _state.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); + 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( + recipient, + currency, + rawBalance, + (currentTotalSupply + value).RawValue); + } + else + { + return UpdateFungibleAssets(recipient, currency, 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); + } + + BigInteger rawBalance = (balance - value).RawValue; + if (currency.TotalSupplyTrackable) + { + var currentTotalSupply = GetTotalSupply(currency); + return UpdateFungibleAssets( + owner, + currency, + rawBalance, + (currentTotalSupply - value).RawValue); + } + else + { + return UpdateFungibleAssets(owner, currency, rawBalance); + } + } + + /// + [Pure] + public IAccount SetValidator(Validator validator) => + UpdateValidatorSet(GetValidatorSet().Update(validator)); + + [Pure] + private MockAccount UpdateState( + Address address, + IValue value) => + new MockAccount( + new MockAccountState( + MockTrie.Add(ToStateKey(address), value)), + TotalUpdatedFungibleAssets); + + [Pure] + private MockAccount UpdateState( + Address address) => + new MockAccount( + new MockAccountState( + MockTrie.Remove(ToStateKey(address))), + TotalUpdatedFungibleAssets); + + [Pure] + private MockAccount UpdateFungibleAssets( + Address address, + Currency currency, + BigInteger amount, + BigInteger? supplyAmount = null) => supplyAmount is { } sa + ? new MockAccount( + new MockAccountState( + MockTrie + .Add(ToFungibleAssetKey(address, currency), new Integer(amount)) + .Add(ToTotalSupplyKey(currency), new Integer(sa))), + TotalUpdatedFungibleAssets.Add((address, currency))) + : new MockAccount( + new MockAccountState( + MockTrie.Add(ToFungibleAssetKey(address, currency), new Integer(amount))), + TotalUpdatedFungibleAssets.Add((address, currency))); + + [Pure] + private MockAccount UpdateValidatorSet(ValidatorSet validatorSet) => + new MockAccount( + new MockAccountState( + MockTrie.Add(ValidatorSetKey, validatorSet.Bencoded)), + TotalUpdatedFungibleAssets); + + [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(sender, currency, (senderBalance - value).RawValue) + .UpdateFungibleAssets(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); + } + + BigInteger senderRawBalance = (senderBalance - value).RawValue; + MockAccount intermediate = UpdateFungibleAssets(sender, currency, senderRawBalance); + FungibleAssetValue recipientBalance = intermediate.GetBalance(recipient, currency); + BigInteger recipientRawBalance = (recipientBalance + value).RawValue; + + return intermediate.UpdateFungibleAssets(recipient, currency, recipientRawBalance); + } + } +} diff --git a/NineChronicles.Headless.Tests/Common/MockAccountState.cs b/NineChronicles.Headless.Tests/Common/MockAccountState.cs new file mode 100644 index 000000000..0523cbc94 --- /dev/null +++ b/NineChronicles.Headless.Tests/Common/MockAccountState.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Store.Trie; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; +using static NineChronicles.Headless.Tests.Common.KeyConverters; + +namespace NineChronicles.Headless.Tests.Common +{ + /// + /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/AccountState.cs + /// except this has its constructors exposed as public for testing. + /// + public class MockAccountState : IAccountState + { + public MockAccountState() : this(ImmutableDictionary.Empty) + { + } + + public MockAccountState(IImmutableDictionary mockTrie) + { + MockTrie = mockTrie; + } + + /// + public ITrie Trie => throw new NotSupportedException(); + + /// + public IImmutableDictionary MockTrie { get; } + + /// + public IValue? GetState(Address address) => MockTrie.TryGetValue(ToStateKey(address), out var v) + ? v + : null; + + /// + public IReadOnlyList GetStates(IReadOnlyList
addresses) => + addresses.Select(address => GetState(address)).ToList(); + + /// + public FungibleAssetValue GetBalance(Address address, Currency currency) + { + IValue? value = MockTrie.TryGetValue(ToFungibleAssetKey(address, currency), out var v) + ? v + : null; + return value is Integer i + ? FungibleAssetValue.FromRawValue(currency, i) + : currency * 0; + } + + /// + public FungibleAssetValue GetTotalSupply(Currency currency) + { + if (!currency.TotalSupplyTrackable) + { + throw TotalSupplyNotTrackableException.WithDefaultMessage(currency); + } + + IValue? value = MockTrie.TryGetValue(ToTotalSupplyKey(currency), out var v) + ? v + : null; + return value is Integer i + ? FungibleAssetValue.FromRawValue(currency, i) + : currency * 0; + } + + /// + public ValidatorSet GetValidatorSet() + { + IValue? value = MockTrie.TryGetValue(ValidatorSetKey, out var v) + ? v + : null; + return value is List list + ? new ValidatorSet(list) + : new ValidatorSet(); + } + + // Methods used in unit tests + public MockAccountState SetState(Address address, IValue state) => + new MockAccountState(MockTrie.SetItem(ToStateKey(address), state)); + + public MockAccountState SetBalance(Address address, FungibleAssetValue amount) => + SetBalance((address, amount.Currency), amount.RawValue); + + public MockAccountState SetBalance(Address address, Currency currency, BigInteger rawAmount) => + SetBalance((address, currency), rawAmount); + + public MockAccountState SetBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) => + new MockAccountState(MockTrie.SetItem(ToFungibleAssetKey(pair), (Integer)rawAmount)); + + public MockAccountState AddBalance(Address address, FungibleAssetValue amount) => + AddBalance((address, amount.Currency), amount.RawValue); + + public MockAccountState AddBalance(Address address, Currency currency, BigInteger rawAmount) => + AddBalance((address, currency), rawAmount); + + public MockAccountState AddBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) + { + var amount = GetBalance(pair.Address, pair.Currency).RawValue + rawAmount; + return SetBalance(pair, amount); + } + + public MockAccountState SubtractBalance(Address address, FungibleAssetValue amount) => + SubtractBalance((address, amount.Currency), amount.RawValue); + + public MockAccountState SubtractBalance(Address address, Currency currency, BigInteger rawAmount) => + SubtractBalance((address, currency), rawAmount); + + public MockAccountState SubtractBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) + { + var amount = GetBalance(pair.Address, pair.Currency).RawValue - rawAmount; + return SetBalance(pair, amount); + } + + public MockAccountState TransferBalance(Address sender, Address recipient, FungibleAssetValue amount) => + TransferBalance(sender, recipient, amount.Currency, amount.RawValue); + + public MockAccountState TransferBalance(Address sender, Address recipient, Currency currency, BigInteger rawAmount) => + SubtractBalance(sender, currency, rawAmount).AddBalance(recipient, currency, rawAmount); + + public MockAccountState SetTotalSupply(FungibleAssetValue amount) => + SetTotalSupply(amount.Currency, amount.RawValue); + + public MockAccountState SetTotalSupply(Currency currency, BigInteger rawAmount) => + currency.TotalSupplyTrackable + ? !(currency.MaximumSupply is { } maximumSupply) || rawAmount <= maximumSupply.RawValue + ? new MockAccountState(MockTrie.SetItem(ToTotalSupplyKey(currency), (Integer)rawAmount)) + : throw new ArgumentException( + $"Given {currency}'s total supply is capped at {maximumSupply.RawValue} and " + + $"cannot be set to {rawAmount}.") + : throw new ArgumentException( + $"Given {currency} is not trackable."); + + public MockAccountState AddTotalSupply(FungibleAssetValue amount) => + AddTotalSupply(amount.Currency, amount.RawValue); + + public MockAccountState AddTotalSupply(Currency currency, BigInteger rawAmount) + { + var amount = GetTotalSupply(currency).RawValue + rawAmount; + return SetTotalSupply(currency, amount); + } + + public MockAccountState SubtractTotalSupply(FungibleAssetValue amount) => + SubtractTotalSupply(amount.Currency, amount.RawValue); + + public MockAccountState SubtractTotalSupply(Currency currency, BigInteger rawAmount) + { + var amount = GetTotalSupply(currency).RawValue - rawAmount; + return SetTotalSupply(currency, amount); + } + + public MockAccountState SetValidator(Validator validator) => + new MockAccountState(MockTrie.SetItem(ValidatorSetKey, GetValidatorSet().Update(validator).Bencoded)); + } +} diff --git a/NineChronicles.Headless.Tests/Common/MockState.cs b/NineChronicles.Headless.Tests/Common/MockState.cs deleted file mode 100644 index 2ca86f1b0..000000000 --- a/NineChronicles.Headless.Tests/Common/MockState.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Store.Trie; -using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; - -namespace NineChronicles.Headless.Tests.Common -{ - /// - /// A mock implementation of with various overloaded methods for - /// improving QoL. - /// - /// - /// All methods are pretty self-explanatory with no side-effects. There are some caveats: - /// - /// - /// Every balance related operation can accept a negative amount. Each behave as expected. - /// That is, adding negative amount would decrease the balance. - /// - /// - /// Negative balance is allowed for all cases. This includes total supply. - /// - /// - /// Total supply is not automatically tracked. That is, changing the balance associated - /// with an does not change the total supply in any way. - /// Total supply must be explicitly set if needed. - /// - /// - /// There are only few restrictions that apply for manipulating this object, mostly - /// pertaining to total supplies: - /// - /// - /// It is not possible to set a total supply amount for a currency that is - /// not trackable. - /// - /// - /// It is not possible to set a total supply amount that is over the currency's - /// capped maximum total supply. - /// - /// - /// - /// - /// - internal class MockState : IAccountState - { - private static readonly MockState _empty = new MockState(); - private readonly IImmutableDictionary _states; - private readonly IImmutableDictionary<(Address, Currency), BigInteger> _fungibles; - private readonly IImmutableDictionary _totalSupplies; - private readonly ValidatorSet _validatorSet; - - private MockState() - : this( - ImmutableDictionary.Empty, - ImmutableDictionary<(Address Address, Currency Currency), BigInteger>.Empty, - ImmutableDictionary.Empty, - new ValidatorSet()) - { - } - - private MockState( - IImmutableDictionary state, - IImmutableDictionary<(Address Address, Currency Currency), BigInteger> balance, - IImmutableDictionary totalSupplies, - ValidatorSet validatorSet) - { - _states = state; - _fungibles = balance; - _totalSupplies = totalSupplies; - _validatorSet = validatorSet; - } - - public static MockState Empty => _empty; - - public IImmutableDictionary States => _states; - - public IImmutableDictionary<(Address, Currency), BigInteger> Fungibles => _fungibles; - - public IImmutableDictionary TotalSupplies => _totalSupplies; - - public ValidatorSet ValidatorSet => _validatorSet; - - public ITrie Trie => throw new NotSupportedException(); - - public IValue? GetState(Address address) => _states.TryGetValue(address, out IValue? value) - ? value - : null; - - public IReadOnlyList GetStates(IReadOnlyList
addresses) => - addresses.Select(GetState).ToArray(); - - public FungibleAssetValue GetBalance(Address address, Currency currency) => - _fungibles.TryGetValue((address, currency), out BigInteger rawValue) - ? FungibleAssetValue.FromRawValue(currency, rawValue) - : FungibleAssetValue.FromRawValue(currency, 0); - - public FungibleAssetValue GetTotalSupply(Currency currency) - { - if (!currency.TotalSupplyTrackable) - { - var msg = - $"The total supply value of the currency {currency} is not trackable" - + " because it is a legacy untracked currency which might have been" - + " established before the introduction of total supply tracking support."; - throw new TotalSupplyNotTrackableException(msg, currency); - } - - return _totalSupplies.TryGetValue(currency, out var rawValue) - ? FungibleAssetValue.FromRawValue(currency, rawValue) - : FungibleAssetValue.FromRawValue(currency, 0); - } - - public ValidatorSet GetValidatorSet() => _validatorSet; - - public MockState SetState(Address address, IValue state) => - new MockState( - _states.SetItem(address, state), - _fungibles, - _totalSupplies, - _validatorSet); - - public MockState SetBalance(Address address, FungibleAssetValue amount) => - SetBalance((address, amount.Currency), amount.RawValue); - - public MockState SetBalance(Address address, Currency currency, BigInteger rawAmount) => - SetBalance((address, currency), rawAmount); - - public MockState SetBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) => - new MockState( - _states, - _fungibles.SetItem(pair, rawAmount), - _totalSupplies, - _validatorSet); - - public MockState AddBalance(Address address, FungibleAssetValue amount) => - AddBalance((address, amount.Currency), amount.RawValue); - - public MockState AddBalance(Address address, Currency currency, BigInteger rawAmount) => - AddBalance((address, currency), rawAmount); - - public MockState AddBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) => - SetBalance(pair, (_fungibles.TryGetValue(pair, out BigInteger amount) ? amount : 0) + rawAmount); - - public MockState SubtractBalance(Address address, FungibleAssetValue amount) => - SubtractBalance((address, amount.Currency), amount.RawValue); - - public MockState SubtractBalance(Address address, Currency currency, BigInteger rawAmount) => - SubtractBalance((address, currency), rawAmount); - - public MockState SubtractBalance((Address Address, Currency Currency) pair, BigInteger rawAmount) => - SetBalance(pair, (_fungibles.TryGetValue(pair, out BigInteger amount) ? amount : 0) - rawAmount); - - public MockState TransferBalance(Address sender, Address recipient, FungibleAssetValue amount) => - TransferBalance(sender, recipient, amount.Currency, amount.RawValue); - - public MockState TransferBalance(Address sender, Address recipient, Currency currency, BigInteger rawAmount) => - SubtractBalance(sender, currency, rawAmount).AddBalance(recipient, currency, rawAmount); - - public MockState SetTotalSupply(FungibleAssetValue amount) => - SetTotalSupply(amount.Currency, amount.RawValue); - - public MockState SetTotalSupply(Currency currency, BigInteger rawAmount) => - currency.TotalSupplyTrackable - ? !(currency.MaximumSupply is { } maximumSupply) || rawAmount <= maximumSupply.RawValue - ? new MockState( - _states, - _fungibles, - _totalSupplies.SetItem(currency, rawAmount), - _validatorSet) - : throw new ArgumentException( - $"Given {currency}'s total supply is capped at {maximumSupply.RawValue} and " + - $"cannot be set to {rawAmount}.") - : throw new ArgumentException( - $"Given {currency} is not trackable."); - - public MockState AddTotalSupply(FungibleAssetValue amount) => - AddTotalSupply(amount.Currency, amount.RawValue); - - public MockState AddTotalSupply(Currency currency, BigInteger rawAmount) => - SetTotalSupply(currency, (_totalSupplies.TryGetValue(currency, out BigInteger amount) ? amount : 0) + rawAmount); - - public MockState SubtractTotalSupply(FungibleAssetValue amount) => - SubtractTotalSupply(amount.Currency, amount.RawValue); - - public MockState SubtractTotalSupply(Currency currency, BigInteger rawAmount) => - SetTotalSupply(currency, (_totalSupplies.TryGetValue(currency, out BigInteger amount) ? amount : 0) - rawAmount); - - public MockState SetValidator(Validator validator) => - new MockState( - _states, - _fungibles, - _totalSupplies, - _validatorSet.Update(validator)); - } -} diff --git a/NineChronicles.Headless.Tests/Common/MockWorld.cs b/NineChronicles.Headless.Tests/Common/MockWorld.cs new file mode 100644 index 000000000..c31e6f723 --- /dev/null +++ b/NineChronicles.Headless.Tests/Common/MockWorld.cs @@ -0,0 +1,83 @@ +using Libplanet.Store.Trie; + +namespace NineChronicles.Headless.Tests.Common +{ + using System; + using System.Diagnostics.Contracts; + using Libplanet.Action.State; + using Libplanet.Crypto; + + /// + /// A rough replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/World.cs + /// except this has its constructors exposed as public for testing. + /// + [Pure] + public class MockWorld : IWorld + { + private readonly IWorldState _baseState; + + public MockWorld(IWorldState baseState) + : this(baseState, new MockWorldDelta()) + { + } + + public MockWorld( + IWorldState baseState, + IWorldDelta delta) + { + _baseState = baseState; + Delta = delta; + Legacy = baseState.Legacy; + } + + /// + public IWorldDelta Delta { get; } + + /// + [Pure] + public ITrie Trie => _baseState.Trie; + + /// + [Pure] + public bool Legacy { get; private set; } + + /// + [Pure] + public IAccount GetAccount(Address address) + { + if (Delta.Accounts.TryGetValue(address, out IAccount? account)) + { + return account; + } + else + { + switch (_baseState.GetAccountState(address)) + { + case MockAccount mockAccount: + return mockAccount; + case MockAccountState mockAccountState: + return new MockAccount(mockAccountState); + default: + throw new InvalidOperationException(); + } + } + } + + public IAccountState GetAccountState(Address address) => GetAccount(address); + + /// + [Pure] + public IWorld SetAccount(Address address, IAccount account) + { + if (!address.Equals(ReservedAddresses.LegacyAccount) + && account.TotalUpdatedFungibleAssets.Count > 0) + { + return this; + } + + return new MockWorld( + this, + Delta.SetAccount(address, account)); + } + } +} diff --git a/NineChronicles.Headless.Tests/Common/MockWorldDelta.cs b/NineChronicles.Headless.Tests/Common/MockWorldDelta.cs new file mode 100644 index 000000000..e354b0acd --- /dev/null +++ b/NineChronicles.Headless.Tests/Common/MockWorldDelta.cs @@ -0,0 +1,62 @@ +using System.Linq; + +namespace NineChronicles.Headless.Tests.Common +{ + using System.Collections.Immutable; + using Libplanet.Action.State; + using Libplanet.Crypto; + + /// + /// Almost a replica of https://github.com/planetarium/libplanet/blob/main/Libplanet.Action/State/WorldDelta.cs + /// except this has its constructors exposed as public for testing. + /// + public class MockWorldDelta : IWorldDelta + { + private IImmutableDictionary _accounts; + + public MockWorldDelta() + { + _accounts = ImmutableDictionary.Empty; + } + + private MockWorldDelta(IImmutableDictionary accounts) + { + _accounts = accounts; + } + + /// + public IImmutableDictionary Accounts + => _accounts + .ToImmutableDictionary(item => item.Key, item => item.Value.Account); + + /// + public IImmutableDictionary Uncommitted + => _accounts + .Where(item => !item.Value.Committed) + .ToImmutableDictionary(item => item.Key, item => item.Value.Account); + + /// + public IWorldDelta SetAccount(Address address, IAccount account) + => new MockWorldDelta(_accounts.SetItem(address, new AccountItem(account, false))); + + /// + public IWorldDelta CommitAccount(Address address) + => _accounts.TryGetValue(address, out AccountItem accountItem) + ? new MockWorldDelta( + _accounts.SetItem(address, new AccountItem(accountItem.Account, true))) + : this; + + internal struct AccountItem + { + public AccountItem(IAccount account, bool committed) + { + Account = account; + Committed = committed; + } + + public IAccount Account { get; } + + public bool Committed { get; set; } + } + } +} diff --git a/NineChronicles.Headless.Tests/Common/MockWorldState.cs b/NineChronicles.Headless.Tests/Common/MockWorldState.cs new file mode 100644 index 000000000..75713585c --- /dev/null +++ b/NineChronicles.Headless.Tests/Common/MockWorldState.cs @@ -0,0 +1,34 @@ +using Libplanet.Store.Trie; + +namespace NineChronicles.Headless.Tests.Common +{ +#nullable enable + + using System.Collections.Immutable; + using Libplanet.Action.State; + using Libplanet.Crypto; + + public class MockWorldState : IWorldState + { + private readonly IImmutableDictionary _accounts; + + public MockWorldState() + : this(ImmutableDictionary.Empty) + { + } + + public MockWorldState(IImmutableDictionary accounts) + { + _accounts = accounts; + } + + public ITrie Trie { get; } + public bool Legacy => true; + + public IImmutableDictionary Accounts => _accounts; + + public IAccountState GetAccountState(Address address) => _accounts.TryGetValue(address, out IAccount? account) + ? account + : new MockAccount(new MockAccountState()); + } +} diff --git a/NineChronicles.Headless.Tests/GraphQLTestUtils.cs b/NineChronicles.Headless.Tests/GraphQLTestUtils.cs index 2b8aab2ed..58c7f7eab 100644 --- a/NineChronicles.Headless.Tests/GraphQLTestUtils.cs +++ b/NineChronicles.Headless.Tests/GraphQLTestUtils.cs @@ -18,6 +18,7 @@ using Nekoyume.Action; using Nekoyume.Action.Loader; using Nekoyume.Model.State; +using Nekoyume.Module; using NineChronicles.Headless.Utils; namespace NineChronicles.Headless.Tests @@ -93,7 +94,7 @@ public static StandaloneContext CreateStandaloneContext() stateStore, genesisBlock, actionEvaluator); - var currencyFactory = new CurrencyFactory(() => blockchain.GetAccountState(blockchain.Tip.Hash)); + var currencyFactory = new CurrencyFactory(() => blockchain.GetWorldState(blockchain.Tip.Hash)); var fungibleAssetValueFactory = new FungibleAssetValueFactory(currencyFactory); return new StandaloneContext { @@ -132,9 +133,9 @@ PrivateKey minerPrivateKey stateStore, genesisBlock, actionEvaluator); - var ncg = new GoldCurrencyState((Dictionary)blockchain.GetState(Addresses.GoldCurrency)) + var ncg = new GoldCurrencyState((Dictionary)blockchain.GetWorldState().GetLegacyState(Addresses.GoldCurrency)) .Currency; - var currencyFactory = new CurrencyFactory(() => blockchain.GetAccountState(blockchain.Tip.Hash), ncg); + var currencyFactory = new CurrencyFactory(() => blockchain.GetWorldState(blockchain.Tip.Hash), ncg); var fungibleAssetValueFactory = new FungibleAssetValueFactory(currencyFactory); return new StandaloneContext { diff --git a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs index 457fbc1cb..47ec4c484 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs @@ -21,6 +21,7 @@ using Nekoyume.Model; using Nekoyume.Model.EnumType; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; using NineChronicles.Headless.GraphTypes; using Xunit; @@ -253,7 +254,7 @@ public async Task TransferAssetWithCurrencyEnum(string currencyType, bool memo) Assert.IsType(plainValue); var actionBase = DeserializeNCAction(plainValue); var action = Assert.IsType(actionBase); - var rawState = _standaloneContext.BlockChain!.GetState(Addresses.GoldCurrency); + var rawState = _standaloneContext.BlockChain!.GetWorldState().GetLegacyState(Addresses.GoldCurrency); var goldCurrencyState = new GoldCurrencyState((Dictionary)rawState); Currency currency = currencyType == "NCG" ? goldCurrencyState.Currency : CrystalCalculator.CRYSTAL; @@ -277,7 +278,7 @@ public async Task TransferAssetWithCurrencyEnum(string currencyType, bool memo) [InlineData("{ ticker: \"CRYSTAL\", minters: [], decimalPlaces: 18 }", false)] public async Task TransferAsset(string valueType, bool memo) { - var rawState = _standaloneContext.BlockChain!.GetState(Addresses.GoldCurrency); + var rawState = _standaloneContext.BlockChain!.GetWorldState().GetLegacyState(Addresses.GoldCurrency); var goldCurrencyState = new GoldCurrencyState((Dictionary)rawState); var recipient = new PrivateKey().Address; @@ -1133,7 +1134,6 @@ public async Task LoadIntoMyGarages( var actualAction = Assert.IsType(actionBase); Assert.True(expectedAction.FungibleAssetValues?.SequenceEqual(actualAction.FungibleAssetValues) ?? actualAction.FungibleAssetValues is null); - Assert.Equal(expectedAction.InventoryAddr, actualAction.InventoryAddr); Assert.True(expectedAction.FungibleIdAndCounts?.SequenceEqual(actualAction.FungibleIdAndCounts) ?? actualAction.FungibleIdAndCounts is null); Assert.Equal(expectedAction.Memo, actualAction.Memo); diff --git a/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs index 9b362c973..4494fa2e5 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StandaloneMutationTest.cs @@ -30,6 +30,7 @@ using Lib9c; using Lib9c.Tests; using Nekoyume; +using Nekoyume.Module; using NineChronicles.Headless.Executable.Commands; using NineChronicles.Headless.Executable.IO; using NineChronicles.Headless.Executable.Tests.IO; @@ -141,9 +142,7 @@ public async Task ActivateAccount() Assert.True(result); Address userAddress = StandaloneContextFx.NineChroniclesNodeService!.MinerPrivateKey!.Address; - IValue? state = BlockChain.GetState( - userAddress.Derive(ActivationKey.DeriveKey) - ); + IValue? state = BlockChain.GetWorldState().GetLegacyState(userAddress.Derive(ActivationKey.DeriveKey)); Assert.True((Bencodex.Types.Boolean)state); } @@ -156,7 +155,7 @@ public async Task Transfer(string? memo, bool error) { NineChroniclesNodeService service = StandaloneContextFx.NineChroniclesNodeService!; Currency goldCurrency = new GoldCurrencyState( - (Dictionary)BlockChain.GetState(GoldCurrencyState.Address) + (Dictionary)BlockChain.GetWorldState().GetLegacyState(GoldCurrencyState.Address) ).Currency; Address senderAddress = service.MinerPrivateKey!.Address; @@ -173,7 +172,7 @@ public async Task Transfer(string? memo, bool error) // 10 + 10 (mining rewards) Assert.Equal( 20 * goldCurrency, - BlockChain.GetBalance(senderAddress, goldCurrency) + BlockChain.GetWorldState().GetBalance(senderAddress, goldCurrency) ); var recipientKey = new PrivateKey(); @@ -227,13 +226,13 @@ public async Task Transfer(string? memo, bool error) // 10 + 10 - 17.5(transfer) Assert.Equal( FungibleAssetValue.Parse(goldCurrency, "2.5"), - BlockChain.GetBalance(senderAddress, goldCurrency) + BlockChain.GetWorldState().GetBalance(senderAddress, goldCurrency) ); // 0 + 17.5(transfer) + 10(mining reward) Assert.Equal( FungibleAssetValue.Parse(goldCurrency, "27.5"), - BlockChain.GetBalance(recipient, goldCurrency) + BlockChain.GetWorldState().GetBalance(recipient, goldCurrency) ); } } @@ -243,7 +242,7 @@ public async Task TransferGold() { NineChroniclesNodeService service = StandaloneContextFx.NineChroniclesNodeService!; Currency goldCurrency = new GoldCurrencyState( - (Dictionary)BlockChain.GetState(GoldCurrencyState.Address) + (Dictionary)BlockChain.GetWorldState().GetLegacyState(GoldCurrencyState.Address) ).Currency; Address senderAddress = service.MinerPrivateKey!.Address; @@ -261,7 +260,7 @@ public async Task TransferGold() // 10 + 10 (mining rewards) Assert.Equal( 20 * goldCurrency, - BlockChain.GetBalance(senderAddress, goldCurrency) + BlockChain.GetWorldState().GetBalance(senderAddress, goldCurrency) ); var recipientKey = new PrivateKey(); @@ -288,13 +287,13 @@ public async Task TransferGold() // 10 + 10 - 17.5(transfer) Assert.Equal( FungibleAssetValue.Parse(goldCurrency, "2.5"), - BlockChain.GetBalance(senderAddress, goldCurrency) + BlockChain.GetWorldState().GetBalance(senderAddress, goldCurrency) ); // 0 + 17.5(transfer) + 10(mining reward) Assert.Equal( FungibleAssetValue.Parse(goldCurrency, "27.5"), - BlockChain.GetBalance(recipient, goldCurrency) + BlockChain.GetWorldState().GetBalance(recipient, goldCurrency) ); } @@ -875,7 +874,7 @@ public async Task Tx_ActivateAccount() var result = (bool)data["stageTx"]; Assert.True(result); - IValue? state = BlockChain.GetState(privateKey.Address.Derive(ActivationKey.DeriveKey)); + IValue? state = BlockChain.GetWorldState().GetLegacyState(privateKey.Address.Derive(ActivationKey.DeriveKey)); Assert.True((Bencodex.Types.Boolean)state); } diff --git a/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs index 1f39c76c3..c9f5e06b3 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StandaloneQueryTest.cs @@ -12,6 +12,7 @@ using Bencodex.Types; using GraphQL.Execution; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Blockchain; using Libplanet.Common; @@ -31,6 +32,7 @@ using Nekoyume.Helper; using Nekoyume.Model; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; using NineChronicles.Headless.Properties; using NineChronicles.Headless.Tests.Common; @@ -53,7 +55,7 @@ public StandaloneQueryTest(ITestOutputHelper output) : base(output) public async Task GetState() { Address adminStateAddress = AdminState.Address; - var result = await ExecuteQueryAsync($"query {{ state(address: \"{adminStateAddress}\") }}"); + var result = await ExecuteQueryAsync($"query {{ state(accountAddress: \"{ReservedAddresses.LegacyAccount}\", address: \"{adminStateAddress}\") }}"); var data = (Dictionary)((ExecutionNode)result.Data!).ToValue()!; IValue rawVal = new Codec().Decode(ByteUtil.ParseHex((string)data!["state"])); AdminState adminState = new AdminState((Dictionary)rawVal); @@ -611,7 +613,7 @@ public async Task TransferNCGHistories(string? memo) lastCommit: GenerateBlockCommit(BlockChain.Tip.Index, BlockChain.Tip.Hash, GenesisValidators)); BlockChain.Append(block, GenerateBlockCommit(block.Index, block.Hash, GenesisValidators)); - var currency = new GoldCurrencyState((Dictionary)BlockChain.GetState(Addresses.GoldCurrency)).Currency; + var currency = new GoldCurrencyState((Dictionary)BlockChain.GetWorldState().GetLegacyState(Addresses.GoldCurrency)).Currency; Transaction MakeTx(ActionBase action) { return BlockChain.MakeTransaction(ProposerPrivateKey, new ActionBase[] { action }); @@ -619,7 +621,6 @@ Transaction MakeTx(ActionBase action) var txs = new[] { MakeTx(new TransferAsset0(sender, recipient, new FungibleAssetValue(currency, 1, 0), memo)), - MakeTx(new TransferAsset2(sender, recipient, new FungibleAssetValue(currency, 1, 0), memo)), MakeTx(new TransferAsset(sender, recipient, new FungibleAssetValue(currency, 1, 0), memo)), }; diff --git a/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs index dbc0120a6..7a96f1766 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -8,6 +9,7 @@ using Bencodex.Types; using GraphQL.Execution; using Lib9c; +using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -90,10 +92,10 @@ public async Task Garage( 2, new Address("0x47D082a115c63E7b58B1532d20E631538eaFADde")); #pragma warning restore CS0618 - MockState mockState = MockState.Empty; + MockAccountState mockAccountState = new MockAccountState(); // NCG - mockState = mockState + mockAccountState = mockAccountState .SetState( Addresses.GoldCurrency, new GoldCurrencyState(goldCurrency).Serialize()); @@ -120,7 +122,7 @@ public async Task Garage( .SetItem("elemental_type", ElementalType.Normal.Serialize()) .SetItem("item_id", HashDigest.FromString(fid).Serialize())); - mockState = mockState + mockAccountState = mockAccountState .SetState( Addresses.GetGarageAddress( agentAddr, @@ -135,7 +137,7 @@ public async Task Garage( // testing without setting up any balance passes the tests; // also this is different from the original test setup as there is no way // to allow state to have "infinite" FAVs with all possible addresses having FAVs - mockState = mockState + mockAccountState = mockAccountState .SetBalance(agentAddr, new FungibleAssetValue(goldCurrency, 99, 99)) .SetBalance(agentAddr, new FungibleAssetValue(Currencies.Crystal, 99, 123456789012345678)) .SetBalance(agentAddr, new FungibleAssetValue(Currencies.Garage, 99, 123456789012345678)) @@ -144,7 +146,10 @@ public async Task Garage( var queryResult = await ExecuteQueryAsync( sb.ToString(), source: new StateContext( - mockState, + new MockWorld(new MockWorldState( + ImmutableDictionary.Empty.Add( + ReservedAddresses.LegacyAccount, + new MockAccount(mockAccountState)))), 0L, new StateMemoryCache())); Assert.Null(queryResult.Errors); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; @@ -237,11 +242,10 @@ public async Task CachedSheet(bool cached, string? expected) cache.SheetCache.SetSheet(cacheKey, (Text)expected, TimeSpan.FromMinutes(1)); } var query = $"{{ cachedSheet(tableName: \"{tableName}\") }}"; - MockState mockState = MockState.Empty; var queryResult = await ExecuteQueryAsync( query, source: new StateContext( - mockState, + new MockWorld(new MockWorldState()), 0L, cache)); Assert.Null(queryResult.Errors); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs index 506212f63..ec601bf20 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AgentStateTypeTest.cs @@ -1,13 +1,16 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading.Tasks; using Bencodex.Types; using GraphQL.Execution; +using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Helper; using Nekoyume.Model.State; +using Nekoyume.Module; using NineChronicles.Headless.GraphTypes.States; using NineChronicles.Headless.Tests.Common; using Xunit; @@ -57,10 +60,9 @@ public async Task Query(int goldBalance, string goldDecimalString, int crystalBa MonsterCollectionState monsterCollectionState = new MonsterCollectionState(monsterCollectionAddress, 7, 0, Fixtures.TableSheetsFX.MonsterCollectionRewardSheet); Address pledgeAddress = agentState.address.GetPledgeAddress(); - MockState mockState = MockState.Empty + MockAccountState mockAccountState = new MockAccountState() .SetState(GoldCurrencyState.Address, new GoldCurrencyState(goldCurrency).Serialize()) .SetState(monsterCollectionAddress, monsterCollectionState.Serialize()) - .SetState(Fixtures.AvatarAddress, Fixtures.AvatarStateFX.Serialize()) .SetState( pledgeAddress, List.Empty @@ -69,10 +71,20 @@ public async Task Query(int goldBalance, string goldDecimalString, int crystalBa .Add(4.Serialize())) .SetBalance(agentState.address, CrystalCalculator.CRYSTAL * crystalBalance) .SetBalance(agentState.address, goldCurrency * goldBalance); + IWorld mockWorld = new MockWorld(new MockWorldState(ImmutableDictionary.Empty.Add( + ReservedAddresses.LegacyAccount, + new MockAccount(mockAccountState)))); + mockWorld = mockWorld.SetAvatarState( + Fixtures.AvatarAddress, + Fixtures.AvatarStateFX, + true, + true, + true, + true); var queryResult = await ExecuteQueryAsync( query, - source: new AgentStateType.AgentStateContext(agentState, mockState, 0, new StateMemoryCache()) + source: new AgentStateType.AgentStateContext(agentState, mockWorld, 0, new StateMemoryCache()) ); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; var expected = new Dictionary() diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs index e21968921..0ebc50e03 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/AvatarStateTypeTest.cs @@ -1,10 +1,15 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using GraphQL.Execution; using Lib9c; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; +using Nekoyume.Module; using NineChronicles.Headless.GraphTypes.States; using NineChronicles.Headless.Tests.Common; using Xunit; @@ -25,14 +30,20 @@ public async Task Query(AvatarState avatarState, Dictionary expe index inventoryAddress }"; - MockState mockState = MockState.Empty - .SetState(Fixtures.AvatarAddress, Fixtures.AvatarStateFX.Serialize()) - .SetState(Fixtures.UserAddress, Fixtures.AgentStateFx.Serialize()); + IWorld mockWorld = new MockWorld(new MockWorldState()); + mockWorld = mockWorld.SetAvatarState( + Fixtures.AvatarAddress, + Fixtures.AvatarStateFX, + true, + true, + true, + true); + mockWorld = mockWorld.SetAgentState(Fixtures.UserAddress, Fixtures.AgentStateFx); var queryResult = await ExecuteQueryAsync( query, source: new AvatarStateType.AvatarStateContext( avatarState, - mockState, + mockWorld, 0, new StateMemoryCache())); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; Assert.Equal(expected, data); @@ -54,14 +65,20 @@ public async Task QueryWithCombinationSlotState(AvatarState avatarState, Diction } } "; - MockState mockState = MockState.Empty - .SetState(Fixtures.AvatarAddress, Fixtures.AvatarStateFX.Serialize()) - .SetState(Fixtures.UserAddress, Fixtures.AgentStateFx.Serialize()); + IWorld mockWorld = new MockWorld(new MockWorldState()); + mockWorld = mockWorld.SetAvatarState( + Fixtures.AvatarAddress, + Fixtures.AvatarStateFX, + true, + true, + true, + true); + mockWorld = mockWorld.SetAgentState(Fixtures.UserAddress, Fixtures.AgentStateFx); for (int i = 0; i < Fixtures.AvatarStateFX.combinationSlotAddresses.Count; i++) { - mockState = mockState - .SetState( + mockWorld = mockWorld + .SetLegacyState( Fixtures.AvatarStateFX.combinationSlotAddresses[i], Fixtures.CombinationSlotStatesFx[i].Serialize()); } @@ -70,7 +87,7 @@ public async Task QueryWithCombinationSlotState(AvatarState avatarState, Diction query, source: new AvatarStateType.AvatarStateContext( avatarState, - mockState, + mockWorld, 0, new StateMemoryCache())); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; Assert.Equal(expected, data); diff --git a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs index dd8ae477d..9619249e9 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/States/Models/StakeStateTypeTest.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading.Tasks; using GraphQL.Execution; +using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume; @@ -25,7 +27,7 @@ public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long var goldCurrency = Currency.Legacy("NCG", 2, null); #pragma warning restore CS0618 - MockState mockState = MockState.Empty + MockAccountState mockAccountState = new MockAccountState() .SetState(GoldCurrencyState.Address, new GoldCurrencyState(goldCurrency).Serialize()) .SetBalance(Fixtures.StakeStateAddress, goldCurrency, (goldCurrency * deposit).RawValue); @@ -43,7 +45,10 @@ public async Task Query(StakeStateV2 stakeState, Address stakeStateAddress, long source: new StakeStateType.StakeStateContext( stakeState, stakeStateAddress, - mockState, + new MockWorld(new MockWorldState( + ImmutableDictionary.Empty.Add( + ReservedAddresses.LegacyAccount, + new MockAccount(mockAccountState)))), blockIndex, new StateMemoryCache())); var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; Assert.Equal(expected, data); diff --git a/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs index dc7d46b51..822579659 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/TransactionHeadlessQueryTest.cs @@ -10,9 +10,7 @@ using GraphQL.Execution; using GraphQL.NewtonsoftJson; using Lib9c; -using Libplanet; using Libplanet.Action; -using Libplanet.Action.Loader; using Libplanet.Action.Sys; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -403,7 +401,7 @@ public async Task NcTransactionsOnTip() private Task ExecuteAsync(string query) { - var currencyFactory = new CurrencyFactory(() => _blockChain.GetAccountState(_blockChain.Tip.Hash)); + var currencyFactory = new CurrencyFactory(() => _blockChain.GetWorldState(_blockChain.Tip.Hash)); var fungibleAssetValueFactory = new FungibleAssetValueFactory(currencyFactory); return GraphQLTestUtils.ExecuteQueryAsync(query, standaloneContext: new StandaloneContext { diff --git a/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs b/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs index c1982fb9c..8078cda5f 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/WorldBossScenarioTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Numerics; using System.Threading.Tasks; using Bencodex.Types; @@ -298,17 +299,20 @@ public async Task RaiderList(bool stateExist, long blockIndex, bool prev) } } - private IAccountState GetMockState() + private IWorldState GetMockState() { - return MockState.Empty - .SetState(_raiderStateAddress, _raiderState.Serialize()) - .SetState(Addresses.GetSheetAddress(), @"id,boss_id,started_block_index,ended_block_index,fee,ticket_price,additional_ticket_price,max_purchase_count + return new MockWorld(new MockWorldState(ImmutableDictionary.Empty.Add( + ReservedAddresses.LegacyAccount, + new MockAccount(new MockAccountState() + .SetState(_raiderStateAddress, _raiderState.Serialize()) + .SetState(Addresses.GetSheetAddress(), + @"id,boss_id,started_block_index,ended_block_index,fee,ticket_price,additional_ticket_price,max_purchase_count 1,205005,0,100,300,200,100,10 2,205005,200,300,300,200,100,10 ".Serialize()) - .SetState(_worldBossAddress, _worldBossState.Serialize()) - .SetState(_worldBossKillRewardRecordAddress, _worldBossKillRewardRecord.Serialize()) - .SetState(_raiderListAddress, List.Empty.Add(_raiderStateAddress.Serialize())); + .SetState(_worldBossAddress, _worldBossState.Serialize()) + .SetState(_worldBossKillRewardRecordAddress, _worldBossKillRewardRecord.Serialize()) + .SetState(_raiderListAddress, List.Empty.Add(_raiderStateAddress.Serialize())))))); } private async Task GetRaidId(long blockIndex, bool prev) diff --git a/NineChronicles.Headless/AccountStateExtension.cs b/NineChronicles.Headless/AccountStateExtension.cs deleted file mode 100644 index 9d12ccfb8..000000000 --- a/NineChronicles.Headless/AccountStateExtension.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Action; -using Nekoyume.Model.State; -using static Lib9c.SerializeKeys; - -namespace NineChronicles.Headless -{ - public static class AccountStateExtension - { - private static readonly string[] AvatarLegacyKeys = - { - LegacyInventoryKey, - LegacyWorldInformationKey, - LegacyQuestListKey, - }; - - public static IReadOnlyList GetAvatarStates( - this IAccountState accountState, - IReadOnlyList
avatarAddresses - ) - { - IReadOnlyDictionary rawAvatarStates = GetRawAvatarStates(accountState, avatarAddresses); - var states = new AvatarState[rawAvatarStates.Count]; - var values = rawAvatarStates.Values.ToArray(); - for (int i = 0; i < rawAvatarStates.Count; i++) - { - states[i] = new AvatarState(values[i]); - } - - return states; - } - - public static AvatarState GetAvatarState(this IAccountState accountState, Address avatarAddress) => - accountState.GetAvatarStates(new[] { avatarAddress })[0]; - - public static IReadOnlyDictionary GetRawAvatarStates( - this IAccountState accountState, - IReadOnlyList
avatarAddresses - ) - { - // Suppose avatarAddresses = [a, b, c] - // Then, addresses = [a, b, c, - // aInventoryKey, bInventoryKey, cInventoryKey, - // aWorldInformationKey, bWorldInformationKey, cWorldInformationKey, - // aQuestListKey, bQuestListKey, cQuestListKey] - var addresses = new Address[avatarAddresses.Count * (AvatarLegacyKeys.Length + 1)]; - for (var i = 0; i < avatarAddresses.Count; i++) - { - var a = avatarAddresses[i]; - addresses[i] = a; - for (int j = 0; j < AvatarLegacyKeys.Length; j++) - { - addresses[avatarAddresses.Count * (j + 1) + i] = a.Derive(AvatarLegacyKeys[j]); - } - } - - IReadOnlyList values = accountState.GetStates(addresses); - var states = new Dictionary(avatarAddresses.Count); - for (var i = 0; i < avatarAddresses.Count; i++) - { - IValue? value = values[i]; - if (!(value is Dictionary serializedAvatar)) - { - throw new InvalidAddressException($"Can't find {nameof(AvatarState)} from {avatarAddresses[i]}"); - } - - Dictionary original = serializedAvatar; - bool v1 = false; - for (int j = 0; j < AvatarLegacyKeys.Length; j++) - { - if (!(values[avatarAddresses.Count * (j + 1) + i] is { } serialized)) - { - v1 = true; - break; - } - - serializedAvatar = serializedAvatar.SetItem(AvatarLegacyKeys[j], serialized); - } - - states[avatarAddresses[i]] = v1 ? original : serializedAvatar; - } - - return states; - } - } -} diff --git a/NineChronicles.Headless/ArenaParticipantsWorker.cs b/NineChronicles.Headless/ArenaParticipantsWorker.cs index 41c8e03ed..33b0d3e82 100644 --- a/NineChronicles.Headless/ArenaParticipantsWorker.cs +++ b/NineChronicles.Headless/ArenaParticipantsWorker.cs @@ -10,15 +10,13 @@ using Microsoft.Extensions.Hosting; using Nekoyume; using Nekoyume.Action; -using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Model.Arena; using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; using NineChronicles.Headless.GraphTypes; -using NineChronicles.Headless.Properties; using Serilog; using static Lib9c.SerializeKeys; @@ -77,12 +75,12 @@ public void GetArenaParticipants() var tip = blockChain.Tip; var blockIndex = blockChain.Tip.Index; - var accountState = blockChain.GetAccountState(tip.Hash); - var currentRoundData = accountState.GetSheet().GetRoundByBlockIndex(blockIndex); + var worldState = blockChain.GetWorldState(tip.Hash); + var currentRoundData = worldState.GetSheet().GetRoundByBlockIndex(blockIndex); var participantsAddr = ArenaParticipants.DeriveAddress( currentRoundData.ChampionshipId, currentRoundData.Round); - var participants = accountState.GetState(participantsAddr) is List participantsList + var participants = worldState.GetLegacyState(participantsAddr) is List participantsList ? new ArenaParticipants(participantsList) : null; var cacheKey = $"{currentRoundData.ChampionshipId}_{currentRoundData.Round}"; @@ -103,7 +101,7 @@ public void GetArenaParticipants() currentRoundData.Round))) .ToArray(); // NOTE: If addresses is too large, and split and get separately. - var scores = accountState.GetStates( + var scores = worldState.GetLegacyStates( avatarAndScoreAddrList.Select(tuple => tuple.Item2).ToList()); var avatarAddrAndScores = new List<(Address avatarAddr, int score)>(); for (int i = 0; i < avatarAddrList.Count; i++) @@ -176,10 +174,10 @@ public void GetArenaParticipants() currentRank + 1)); } - var runeListSheet = accountState.GetSheet(); - var costumeSheet = accountState.GetSheet(); - var characterSheet = accountState.GetSheet(); - var runeOptionSheet = accountState.GetSheet(); + var runeListSheet = worldState.GetSheet(); + var costumeSheet = worldState.GetSheet(); + var characterSheet = worldState.GetSheet(); + var runeOptionSheet = worldState.GetSheet(); var runeIds = runeListSheet.Values.Select(x => x.Id).ToList(); var row = characterSheet[GameConfig.DefaultAvatarCharacterId]; var addrBulk = avatarAddrAndScoresWithRank @@ -197,33 +195,19 @@ public void GetArenaParticipants() addrBulk.AddRange(runeIds.Select(x => RuneState.DeriveAddress(tuple.avatarAddr, x))); } - var states = accountState.GetStates(addrBulk); - var stateBulk = new Dictionary(); - for (int i = 0; i < addrBulk.Count; i++) - { - var address = addrBulk[i]; - var value = states[i]; - stateBulk.TryAdd(address, value ?? Null.Value); - } var runeStates = new List(); var result = avatarAddrAndScoresWithRank.Select(tuple => { var (avatarAddr, score, rank) = tuple; - var avatar = new AvatarState((Dictionary)stateBulk[avatarAddr]); - if (stateBulk[avatarAddr.Derive(LegacyInventoryKey)] is List inventoryList) - { - var inventory = new Inventory(inventoryList); - avatar.inventory = inventory; - } - + var avatar = worldState.GetAvatarState(avatarAddr); var itemSlotState = - stateBulk[ItemSlotState.DeriveAddress(avatarAddr, BattleType.Arena)] is + worldState.GetLegacyState(ItemSlotState.DeriveAddress(avatarAddr, BattleType.Arena)) is List itemSlotList ? new ItemSlotState(itemSlotList) : new ItemSlotState(BattleType.Arena); var runeSlotState = - stateBulk[RuneSlotState.DeriveAddress(avatarAddr, BattleType.Arena)] is + worldState.GetLegacyState(RuneSlotState.DeriveAddress(avatarAddr, BattleType.Arena)) is List runeSlotList ? new RuneSlotState(runeSlotList) : new RuneSlotState(BattleType.Arena); @@ -232,7 +216,7 @@ List runeSlotList foreach (var id in runeIds) { var address = RuneState.DeriveAddress(avatarAddr, id); - if (stateBulk[address] is List runeStateList) + if (worldState.GetLegacyState(address) is List runeStateList) { runeStates.Add(new RuneState(runeStateList)); } diff --git a/NineChronicles.Headless/BlockChainService.cs b/NineChronicles.Headless/BlockChainService.cs index a05b128f7..237c3e9f0 100644 --- a/NineChronicles.Headless/BlockChainService.cs +++ b/NineChronicles.Headless/BlockChainService.cs @@ -20,13 +20,13 @@ using Libplanet.Types.Tx; using MagicOnion; using MagicOnion.Server; -using MessagePack; using Microsoft.Extensions.Caching.Memory; using Nekoyume; -using Nekoyume.Shared.Services; -using Serilog; using Nekoyume.Model.State; +using Nekoyume.Module; +using Nekoyume.Shared.Services; using Sentry; +using Serilog; using static NineChronicles.Headless.NCActionUtils; using NodeExceptionType = Libplanet.Headless.NodeExceptionType; using Transaction = Libplanet.Types.Tx.Transaction; @@ -122,70 +122,124 @@ public UnaryResult PutTransaction(byte[] txBytes) } } - public UnaryResult GetState(byte[] addressBytes, byte[] blockHashBytes) + public UnaryResult GetStateByBlockHash( + byte[] blockHashBytes, + byte[] accountAddressBytes, + byte[] addressBytes) { - var address = new Address(addressBytes); var hash = new BlockHash(blockHashBytes); - IValue state = _blockChain.GetAccountState(hash).GetState(address); + var accountAddress = new Address(accountAddressBytes); + var address = new Address(addressBytes); + IValue state = _blockChain + .GetWorldState(hash) + .GetAccountState(accountAddress) + .GetState(address); // FIXME: Null과 null 구분해서 반환해야 할 듯 byte[] encoded = _codec.Encode(state ?? Null.Value); return new UnaryResult(encoded); } - public UnaryResult GetStateBySrh(byte[] addressBytes, byte[] stateRootHashBytes) + public UnaryResult GetStateByStateRootHash( + byte[] stateRootHashBytes, + byte[] accountAddressBytes, + byte[] addressBytes) { var stateRootHash = new HashDigest(stateRootHashBytes); + var accountAddress = new Address(accountAddressBytes); var address = new Address(addressBytes); - IValue state = _blockChain.GetAccountState(stateRootHash).GetState(address); + IValue state = _blockChain + .GetWorldState(stateRootHash) + .GetAccountState(accountAddress) + .GetState(address); byte[] encoded = _codec.Encode(state ?? Null.Value); return new UnaryResult(encoded); } - public async UnaryResult> GetAvatarStates(IEnumerable addressBytesList, byte[] blockHashBytes) + public async UnaryResult> GetAgentStatesByBlockHash( + byte[] blockHashBytes, + IEnumerable addressBytesList) { var hash = new BlockHash(blockHashBytes); - var accountState = _blockChain.GetAccountState(hash); + var worldState = _blockChain.GetWorldState(hash); var result = new ConcurrentDictionary(); - var addresses = addressBytesList.Select(a => new Address(a)).ToList(); - var rawAvatarStates = accountState.GetRawAvatarStates(addresses); - var taskList = rawAvatarStates - .Select(pair => Task.Run(() => - { - result.TryAdd(pair.Key.ToByteArray(), _codec.Encode(pair.Value)); - })) - .ToList(); + var taskList = addressBytesList.Select(addressByte => Task.Run(() => + { + var value = worldState.GetResolvedState(new Address(addressByte), Addresses.Agent); + result.TryAdd(addressByte, _codec.Encode(value ?? Null.Value)); + })); await Task.WhenAll(taskList); return result.ToDictionary(kv => kv.Key, kv => kv.Value); } - public async UnaryResult> GetAvatarStatesBySrh( - IEnumerable addressBytesList, - byte[] stateRootHashBytes) + public async UnaryResult> GetAgentStatesByStateRootHash( + byte[] stateRootHashBytes, + IEnumerable addressBytesList) { var stateRootHash = new HashDigest(stateRootHashBytes); - var accountState = _blockChain.GetAccountState(stateRootHash); + var worldState = _blockChain.GetWorldState(stateRootHash); var result = new ConcurrentDictionary(); - var addresses = addressBytesList.Select(a => new Address(a)).ToList(); - var rawAvatarStates = accountState.GetRawAvatarStates(addresses); - var taskList = rawAvatarStates - .Select(pair => Task.Run(() => - { - result.TryAdd(pair.Key.ToByteArray(), _codec.Encode(pair.Value)); - })) - .ToList(); + var taskList = addressBytesList.Select(addressByte => Task.Run(() => + { + var value = worldState.GetResolvedState(new Address(addressByte), Addresses.Agent); + result.TryAdd(addressByte, _codec.Encode(value ?? Null.Value)); + })); await Task.WhenAll(taskList); return result.ToDictionary(kv => kv.Key, kv => kv.Value); } - public UnaryResult> GetStateBulk(IEnumerable addressBytesList, byte[] blockHashBytes) + public async UnaryResult> GetAvatarStatesByBlockHash( + byte[] blockHashBytes, + IEnumerable addressBytesList) { var hash = new BlockHash(blockHashBytes); + var worldState = _blockChain.GetWorldState(hash); + var result = new ConcurrentDictionary(); + var addresses = addressBytesList.Select(a => new Address(a)).ToList(); + var taskList = addresses.Select(address => Task.Run(() => + { + var value = worldState.GetFullAvatarStateRaw(address); + result.TryAdd(address.ToByteArray(), _codec.Encode(value ?? Null.Value)); + })); + + await Task.WhenAll(taskList); + return result.ToDictionary(kv => kv.Key, kv => kv.Value); + } + + public async UnaryResult> GetAvatarStatesByStateRootHash( + byte[] stateRootHashBytes, + IEnumerable addressBytesList) + { + var addresses = addressBytesList.Select(a => new Address(a)).ToList(); + var stateRootHash = new HashDigest(stateRootHashBytes); + var worldState = _blockChain.GetWorldState(stateRootHash); + var result = new ConcurrentDictionary(); + var taskList = addresses.Select(address => Task.Run(() => + { + var value = worldState.GetFullAvatarStateRaw(address); + result.TryAdd(address.ToByteArray(), _codec.Encode(value ?? Null.Value)); + })); + + await Task.WhenAll(taskList); + return result.ToDictionary(kv => kv.Key, kv => kv.Value); + } + + public UnaryResult> GetBulkStateByBlockHash( + byte[] blockHashBytes, + byte[] accountAddressBytes, + IEnumerable addressBytesList) + { + var blockHash = new BlockHash(blockHashBytes); + var accountAddress = new Address(accountAddressBytes); + List
addresses = addressBytesList.Select(b => new Address(b)).ToList(); + var result = new Dictionary(); - Address[] addresses = addressBytesList.Select(b => new Address(b)).ToArray(); - IReadOnlyList values = _blockChain.GetStates(addresses, hash); - for (int i = 0; i < addresses.Length; i++) + IReadOnlyList values = _blockChain + .GetWorldState(blockHash) + .GetAccountState(accountAddress) + .GetStates(addresses); + for (int i = 0; i < addresses.Count; i++) { result.TryAdd(addresses[i].ToByteArray(), _codec.Encode(values[i] ?? Null.Value)); } @@ -193,15 +247,21 @@ public UnaryResult> GetStateBulk(IEnumerable return new UnaryResult>(result); } - public UnaryResult> GetStateBulkBySrh( - IEnumerable addressBytesList, - byte[] stateRootHashBytes) + public UnaryResult> GetBulkStateByStateRootHash( + byte[] stateRootHashBytes, + byte[] accountAddressBytes, + IEnumerable addressBytesList) { var stateRootHash = new HashDigest(stateRootHashBytes); + var accountAddress = new Address(accountAddressBytes); + List
addresses = addressBytesList.Select(b => new Address(b)).ToList(); + var result = new Dictionary(); - Address[] addresses = addressBytesList.Select(b => new Address(b)).ToArray(); - IReadOnlyList values = _blockChain.GetAccountState(stateRootHash).GetStates(addresses); - for (int i = 0; i < addresses.Length; i++) + IReadOnlyList values = _blockChain + .GetWorldState(stateRootHash) + .GetAccountState(accountAddress) + .GetStates(addresses); + for (int i = 0; i < addresses.Count; i++) { result.TryAdd(addresses[i].ToByteArray(), _codec.Encode(values[i] ?? Null.Value)); } @@ -210,8 +270,8 @@ public UnaryResult> GetStateBulkBySrh( } public UnaryResult> GetSheets( - IEnumerable addressBytesList, - byte[] stateRootHashBytes) + byte[] stateRootHashBytes, + IEnumerable addressBytesList) { var started = DateTime.UtcNow; var sw = new Stopwatch(); @@ -236,7 +296,7 @@ public UnaryResult> GetSheets( if (addresses.Any()) { var stateRootHash = new BlockHash(stateRootHashBytes); - IReadOnlyList values = _blockChain.GetAccountState(stateRootHash).GetStates(addresses); + IReadOnlyList values = _blockChain.GetWorldState(stateRootHash).GetLegacyStates(addresses); sw.Stop(); Log.Information("[GetSheets]Get sheet from state: {Count}, Elapsed: {Elapsed}", addresses.Count, sw.Elapsed); sw.Restart(); @@ -252,13 +312,19 @@ public UnaryResult> GetSheets( return new UnaryResult>(result); } - public UnaryResult GetBalance(byte[] addressBytes, byte[] currencyBytes, byte[] blockHashBytes) + public UnaryResult GetBalanceByBlockHash( + byte[] blockHashBytes, + byte[] addressBytes, + byte[] currencyBytes) { + var blockHash = new BlockHash(blockHashBytes); var address = new Address(addressBytes); var serializedCurrency = (Bencodex.Types.Dictionary)_codec.Decode(currencyBytes); Currency currency = CurrencyExtensions.Deserialize(serializedCurrency); - var hash = new BlockHash(blockHashBytes); - FungibleAssetValue balance = _blockChain.GetAccountState(hash).GetBalance(address, currency); + FungibleAssetValue balance = _blockChain + .GetWorldState(blockHash) + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetBalance(address, currency); byte[] encoded = _codec.Encode( new Bencodex.Types.List( new IValue[] @@ -271,13 +337,19 @@ public UnaryResult GetBalance(byte[] addressBytes, byte[] currencyBytes, return new UnaryResult(encoded); } - public UnaryResult GetBalanceBySrh(byte[] addressBytes, byte[] currencyBytes, byte[] stateRootHashBytes) + public UnaryResult GetBalanceByStateRootHash( + byte[] stateRootHashBytes, + byte[] addressBytes, + byte[] currencyBytes) { - var address = new Address(addressBytes); var stateRootHash = new HashDigest(stateRootHashBytes); + var address = new Address(addressBytes); var serializedCurrency = (Bencodex.Types.Dictionary)_codec.Decode(currencyBytes); Currency currency = CurrencyExtensions.Deserialize(serializedCurrency); - FungibleAssetValue balance = _blockChain.GetAccountState(stateRootHash).GetBalance(address, currency); + FungibleAssetValue balance = _blockChain + .GetWorldState(stateRootHash) + .GetAccountState(ReservedAddresses.LegacyAccount) + .GetBalance(address, currency); byte[] encoded = _codec.Encode( new Bencodex.Types.List( new IValue[] diff --git a/NineChronicles.Headless/GraphQLServiceExtensions.cs b/NineChronicles.Headless/GraphQLServiceExtensions.cs index 7f0a24af0..d717a0153 100644 --- a/NineChronicles.Headless/GraphQLServiceExtensions.cs +++ b/NineChronicles.Headless/GraphQLServiceExtensions.cs @@ -50,6 +50,7 @@ public static IServiceCollection AddLibplanetScalarTypes(this IServiceCollection services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton>(); services.TryAddSingleton>(); diff --git a/NineChronicles.Headless/GraphTypes/ActionMutation.cs b/NineChronicles.Headless/GraphTypes/ActionMutation.cs index 41fbbe19c..17e9f7e66 100644 --- a/NineChronicles.Headless/GraphTypes/ActionMutation.cs +++ b/NineChronicles.Headless/GraphTypes/ActionMutation.cs @@ -1,4 +1,3 @@ -using Bencodex.Types; using GraphQL; using GraphQL.Types; using Libplanet.Crypto; @@ -10,6 +9,7 @@ using Serilog; using System; using System.Collections.Generic; +using Nekoyume.Module; namespace NineChronicles.Headless.GraphTypes { diff --git a/NineChronicles.Headless/GraphTypes/ActionQuery.cs b/NineChronicles.Headless/GraphTypes/ActionQuery.cs index 7e092c6ba..81d34b1db 100644 --- a/NineChronicles.Headless/GraphTypes/ActionQuery.cs +++ b/NineChronicles.Headless/GraphTypes/ActionQuery.cs @@ -13,6 +13,7 @@ using Nekoyume.Action; using Nekoyume.Model; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; namespace NineChronicles.Headless.GraphTypes @@ -437,7 +438,7 @@ public ActionQuery(StandaloneContext standaloneContext) { var activationCode = context.GetArgument("activationCode"); var activationKey = ActivationKey.Decode(activationCode); - if (standaloneContext.BlockChain!.GetState(activationKey.PendingAddress) is Dictionary dictionary) + if (standaloneContext.BlockChain!.GetWorldState().GetLegacyState(activationKey.PendingAddress) is Dictionary dictionary) { var pending = new PendingActivationState(dictionary); var action = activationKey.CreateActivateAccount(pending.Nonce); diff --git a/NineChronicles.Headless/GraphTypes/ActivationStatusMutation.cs b/NineChronicles.Headless/GraphTypes/ActivationStatusMutation.cs index 00eb229d2..4575105f5 100644 --- a/NineChronicles.Headless/GraphTypes/ActivationStatusMutation.cs +++ b/NineChronicles.Headless/GraphTypes/ActivationStatusMutation.cs @@ -2,9 +2,11 @@ using Bencodex.Types; using GraphQL; using GraphQL.Types; +using Libplanet.Action; using Nekoyume.Action; using Nekoyume.Model; using Nekoyume.Model.State; +using Nekoyume.Module; namespace NineChronicles.Headless.GraphTypes { @@ -39,7 +41,7 @@ public ActivationStatusMutation(NineChroniclesNodeService service) throw new InvalidOperationException($"{nameof(blockChain)} is null."); } - IValue state = blockChain.GetState(activationKey.PendingAddress); + IValue state = blockChain.GetWorldState().GetLegacyState(activationKey.PendingAddress); if (!(state is Bencodex.Types.Dictionary asDict)) { @@ -51,7 +53,7 @@ public ActivationStatusMutation(NineChroniclesNodeService service) ActivateAccount action = activationKey.CreateActivateAccount( pendingActivationState.Nonce); - var actions = new ActionBase[] { action }; + var actions = new IAction[] { action }; blockChain.MakeTransaction(privateKey, actions); } catch (ArgumentException ae) diff --git a/NineChronicles.Headless/GraphTypes/ActivationStatusQuery.cs b/NineChronicles.Headless/GraphTypes/ActivationStatusQuery.cs index b0717d119..542fcdc69 100644 --- a/NineChronicles.Headless/GraphTypes/ActivationStatusQuery.cs +++ b/NineChronicles.Headless/GraphTypes/ActivationStatusQuery.cs @@ -7,6 +7,7 @@ using Nekoyume.Model.State; using System; using Libplanet.Explorer.GraphTypes; +using Nekoyume.Module; using Log = Serilog.Log; namespace NineChronicles.Headless.GraphTypes @@ -44,14 +45,14 @@ public ActivationStatusQuery(StandaloneContext standaloneContext) Address userAddress = privateKey.Address; Address activatedAddress = userAddress.Derive(ActivationKey.DeriveKey); - if (blockChain.GetState(activatedAddress) is Bencodex.Types.Boolean) + if (blockChain.GetWorldState().GetLegacyState(activatedAddress) is Bencodex.Types.Boolean) { return true; } // Preserve previous check code due to migration period. // TODO: Remove this code after v100061+ - IValue state = blockChain.GetState(ActivatedAccountsState.Address); + IValue state = blockChain.GetWorldState().GetLegacyState(ActivatedAccountsState.Address); if (state is Bencodex.Types.Dictionary asDict) { @@ -101,14 +102,14 @@ public ActivationStatusQuery(StandaloneContext standaloneContext) var userAddress = context.GetArgument
("address"); Address activatedAddress = userAddress.Derive(ActivationKey.DeriveKey); - if (blockChain.GetState(activatedAddress) is Bencodex.Types.Boolean) + if (blockChain.GetWorldState().GetLegacyState(activatedAddress) is Bencodex.Types.Boolean) { return true; } // backward for launcher E2E test. // TODO: Remove this code after launcher E2E test fixed. - IValue state = blockChain.GetState(ActivatedAccountsState.Address); + IValue state = blockChain.GetWorldState().GetLegacyState(ActivatedAccountsState.Address); if (state is Bencodex.Types.Dictionary asDict) { diff --git a/NineChronicles.Headless/GraphTypes/StandaloneMutation.cs b/NineChronicles.Headless/GraphTypes/StandaloneMutation.cs index dd1b523c7..70ef414e6 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneMutation.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneMutation.cs @@ -13,6 +13,7 @@ using Nekoyume.Model.State; using Serilog; using System; +using Nekoyume.Module; namespace NineChronicles.Headless.GraphTypes { @@ -194,9 +195,8 @@ IConfiguration configuration } BlockChain blockChain = service.BlockChain; - var currency = new GoldCurrencyState( - (Dictionary)blockChain.GetState(new Address(context.GetArgument("currencyAddress"))) - ).Currency; + var currency = new GoldCurrencyState((Dictionary)blockChain.GetWorldState() + .GetLegacyState(new Address(context.GetArgument("currencyAddress")))).Currency; FungibleAssetValue amount = FungibleAssetValue.Parse(currency, context.GetArgument("amount")); @@ -253,7 +253,7 @@ IConfiguration configuration BlockChain blockChain = service.BlockChain; var currency = new GoldCurrencyState( - (Dictionary)blockChain.GetState(GoldCurrencyState.Address) + (Dictionary)blockChain.GetWorldState().GetLegacyState(GoldCurrencyState.Address) ).Currency; FungibleAssetValue amount = FungibleAssetValue.Parse(currency, context.GetArgument("amount")); diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs index 869f38e04..afd2f7471 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs @@ -7,6 +7,7 @@ using GraphQL; using GraphQL.Types; using Lib9c; +using Libplanet.Action.State; using Libplanet.Blockchain; using Libplanet.Common; using Libplanet.Crypto; @@ -20,6 +21,7 @@ using Nekoyume.Model.State; using Nekoyume.TableData; using Nekoyume.Model; +using Nekoyume.Module; using NineChronicles.Headless.GraphTypes.States; using static NineChronicles.Headless.NCActionUtils; using Transaction = Libplanet.Types.Tx.Transaction; @@ -56,7 +58,7 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi } return new StateContext( - chain.GetAccountState(blockHash), + chain.GetWorldState(blockHash), blockHash switch { BlockHash bh => chain[bh].Index, @@ -70,8 +72,9 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi Field( name: "state", arguments: new QueryArguments( - new QueryArgument> { Name = "address", Description = "The address of state to fetch from the chain." }, - new QueryArgument { Name = "hash", Description = "The hash of the block used to fetch state from chain." } + new QueryArgument { Name = "hash", Description = "The hash of the block used to fetch state from chain." }, + new QueryArgument> { Name = "accountAddress", Description = "The address of account to fetch from the chain." }, + new QueryArgument> { Name = "address", Description = "The address of state to fetch from the account." } ), resolve: context => { @@ -81,13 +84,17 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!"); } - var address = context.GetArgument
("address"); var blockHashByteArray = context.GetArgument("hash"); var blockHash = blockHashByteArray is null ? blockChain.Tip.Hash : new BlockHash(blockHashByteArray); + var accountAddress = context.GetArgument
("accountAddress"); + var address = context.GetArgument
("address"); - var state = blockChain.GetStates(new[] { address }, blockHash)[0]; + var state = blockChain + .GetWorldState(blockHash) + .GetAccountState(accountAddress) + .GetState(address); if (state is null) { @@ -204,13 +211,12 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi ? blockChain.Tip.Hash : new BlockHash(blockHashByteArray); Currency currency = new GoldCurrencyState( - (Dictionary)blockChain.GetState(GoldCurrencyState.Address) + (Dictionary)blockChain.GetWorldState(blockHash).GetLegacyState(GoldCurrencyState.Address) ).Currency; - return blockChain.GetBalance( + return blockChain.GetWorldState(blockHash).GetBalance( address, - currency, - blockHash + currency ).GetQuantityString(); } ); @@ -308,24 +314,22 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi BlockHash offset = blockChain.Tip.Hash; + IWorldState worldState = blockChain.GetWorldState(offset); #pragma warning disable S3247 - if (blockChain.GetStates(new[] { agentAddress }, offset)[0] is Dictionary agentDict) + if (worldState.GetAgentState(agentAddress) is { } agentState) #pragma warning restore S3247 { - AgentState agentState = new AgentState(agentDict); - Address deriveAddress = MonsterCollectionState.DeriveAddress(agentAddress, agentState.MonsterCollectionRound); + Address deriveAddress = + MonsterCollectionState.DeriveAddress(agentAddress, agentState.MonsterCollectionRound); Currency currency = new GoldCurrencyState( - (Dictionary)blockChain.GetStates(new[] { Addresses.GoldCurrency }, offset)[0] - ).Currency; + (Dictionary)worldState.GetLegacyState(Addresses.GoldCurrency)).Currency; - FungibleAssetValue balance = blockChain.GetBalance(agentAddress, currency, offset); - if (blockChain.GetStates(new[] { deriveAddress }, offset)[0] is Dictionary mcDict) + FungibleAssetValue balance = worldState.GetBalance(agentAddress, currency); + if (worldState.GetLegacyState(deriveAddress) is Dictionary mcDict) { var rewardSheet = new MonsterCollectionRewardSheet(); - var csv = blockChain.GetStates( - new[] { Addresses.GetSheetAddress() }, - offset - )[0].ToDotnetString(); + var csv = worldState.GetLegacyState( + Addresses.GetSheetAddress()).ToDotnetString(); rewardSheet.Set(csv); var monsterCollectionState = new MonsterCollectionState(mcDict); long tipIndex = blockChain.Tip.Index; @@ -371,7 +375,7 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi string invitationCode = context.GetArgument("invitationCode"); ActivationKey activationKey = ActivationKey.Decode(invitationCode); - if (blockChain.GetState(activationKey.PendingAddress) is Dictionary dictionary) + if (blockChain.GetWorldState().GetLegacyState(activationKey.PendingAddress) is Dictionary dictionary) { var pending = new PendingActivationState(dictionary); ActivateAccount action = activationKey.CreateActivateAccount(pending.Nonce); @@ -415,7 +419,7 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi { throw new ExecutionError("invitationCode format is invalid."); } - if (blockChain.GetState(activationKey.PendingAddress) is Dictionary dictionary) + if (blockChain.GetWorldState().GetLegacyState(activationKey.PendingAddress) is Dictionary dictionary) { var pending = new PendingActivationState(dictionary); return ByteUtil.Hex(pending.Nonce); diff --git a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs index 90f43e81c..d549fcad3 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs @@ -9,7 +9,6 @@ using Libplanet.Net; using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Linq; @@ -27,7 +26,9 @@ using Libplanet.Store; using Libplanet.Types.Tx; using Microsoft.Extensions.Configuration; +using Nekoyume.Module; using Serilog; +using Libplanet.Action.State; namespace NineChronicles.Headless.GraphTypes { @@ -335,13 +336,11 @@ private void RenderBlock((Block OldTip, Block NewTip) pair) BlockChain blockChain = StandaloneContext.NineChroniclesNodeService.BlockChain; Currency currency = new GoldCurrencyState( - (Dictionary)blockChain.GetStates(new[] { Addresses.GoldCurrency }, _tipHeader.Hash)[0] + (Dictionary)blockChain.GetWorldState(_tipHeader.Hash).GetLegacyState(Addresses.GoldCurrency) ).Currency; var rewardSheet = new MonsterCollectionRewardSheet(); - var csv = blockChain.GetStates( - new[] { Addresses.GetSheetAddress() }, - _tipHeader.Hash - )[0].ToDotnetString(); + var csv = blockChain.GetWorldState(_tipHeader.Hash) + .GetLegacyState(Addresses.GetSheetAddress()).ToDotnetString(); rewardSheet.Set(csv); Log.Debug($"StandaloneSubscription.RenderBlock target addresses. (count: {StandaloneContext.AgentAddresses.Count})"); StandaloneContext.AgentAddresses @@ -376,15 +375,14 @@ private void RenderForAgent( ReplaySubject balanceSubject, MonsterCollectionRewardSheet rewardSheet) { - FungibleAssetValue agentBalance = blockChain.GetBalance(address, currency, tipHeader.Hash); + FungibleAssetValue agentBalance = blockChain.GetWorldState(tipHeader.Hash).GetBalance(address, currency); balanceSubject.OnNext(agentBalance.GetQuantityString(true)); - if (blockChain.GetStates(new[] { address }, tipHeader.Hash)[0] is Dictionary rawAgent) + if (blockChain.GetWorldState(tipHeader.Hash).GetAgentState(address) is { } agentState) { - AgentState agentState = new AgentState(rawAgent); Address deriveAddress = MonsterCollectionState.DeriveAddress(address, agentState.MonsterCollectionRound); if (agentState.avatarAddresses.Any() && - blockChain.GetStates(new[] { deriveAddress }, tipHeader.Hash)[0] is Dictionary collectDict) + blockChain.GetWorldState(tipHeader.Hash).GetLegacyState(deriveAddress) is Dictionary collectDict) { var monsterCollectionState = new MonsterCollectionState(collectDict); List rewards = monsterCollectionState.CalculateRewards( diff --git a/NineChronicles.Headless/GraphTypes/StateQuery.cs b/NineChronicles.Headless/GraphTypes/StateQuery.cs index d088fb5af..d1b7b4de9 100644 --- a/NineChronicles.Headless/GraphTypes/StateQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StateQuery.cs @@ -18,6 +18,7 @@ using Nekoyume.Model.Item; using Nekoyume.Model.Stake; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; using Nekoyume.TableData.Crystal; using Nekoyume.TableData.Stake; @@ -43,8 +44,8 @@ public StateQuery() try { return new AvatarStateType.AvatarStateContext( - context.AccountState.GetAvatarState(address), - context.AccountState, + context.WorldState.GetAvatarState(address), + context.WorldState, context.BlockIndex, context.StateMemoryCache); } catch (InvalidAddressException) @@ -97,7 +98,7 @@ public StateQuery() resolve: context => { var index = context.GetArgument("index"); - if (context.Source.GetState(RankingState.Derive(index)) is { } state) + if (context.Source.WorldState.GetLegacyState(RankingState.Derive(index)) is { } state) { return new RankingMapState((Dictionary)state); } @@ -108,7 +109,7 @@ public StateQuery() name: "shop", description: "State for shop.", deprecationReason: "Shop is migrated to ShardedShop and not using now. Use shardedShop() instead.", - resolve: context => context.Source.GetState(Addresses.Shop) is { } state + resolve: context => context.Source.WorldState.GetLegacyState(Addresses.Shop) is { } state ? new ShopState((Dictionary)state) : null); Field( @@ -130,7 +131,7 @@ public StateQuery() var subType = context.GetArgument("itemSubType"); var nonce = context.GetArgument("nonce").ToString("X").ToLower(); - if (context.Source.GetState(ShardedShopStateV2.DeriveAddress(subType, nonce)) is { } state) + if (context.Source.WorldState.GetLegacyState(ShardedShopStateV2.DeriveAddress(subType, nonce)) is { } state) { return new ShardedShopStateV2((Dictionary)state); } @@ -150,20 +151,20 @@ public StateQuery() { var index = context.GetArgument("index"); var arenaAddress = WeeklyArenaState.DeriveAddress(index); - if (context.Source.GetState(arenaAddress) is { } state) + if (context.Source.WorldState.GetLegacyState(arenaAddress) is { } state) { var arenastate = new WeeklyArenaState((Dictionary)state); if (arenastate.OrderedArenaInfos.Count == 0) { var listAddress = arenaAddress.Derive("address_list"); - if (context.Source.GetState(listAddress) is List rawList) + if (context.Source.WorldState.GetLegacyState(listAddress) is List rawList) { var addressList = rawList.ToList(StateExtensions.ToAddress); var arenaInfos = new List(); foreach (var address in addressList) { var infoAddress = arenaAddress.Derive(address.ToByteArray()); - if (context.Source.GetState(infoAddress) is Dictionary rawInfo) + if (context.Source.WorldState.GetLegacyState(infoAddress) is Dictionary rawInfo) { var info = new ArenaInfo(rawInfo); arenaInfos.Add(info); @@ -213,8 +214,8 @@ public StateQuery() return ( address, - new ArenaInformation((List)context.Source.GetState(infoAddr)!), - new ArenaScore((List)context.Source.GetState(scoreAddr)!) + new ArenaInformation((List)context.Source.WorldState.GetLegacyState(infoAddr)!), + new ArenaScore((List)context.Source.WorldState.GetLegacyState(scoreAddr)!) ); } ); @@ -231,11 +232,11 @@ public StateQuery() resolve: context => { var address = context.GetArgument
("address"); - if (context.Source.GetState(address) is Dictionary state) + if (context.Source.WorldState.GetAgentState(address) is { } agentState) { return new AgentStateType.AgentStateContext( - new AgentState(state), - context.Source.AccountState, + agentState, + context.Source.WorldState, context.Source.BlockIndex, context.Source.StateMemoryCache ); @@ -248,12 +249,12 @@ public StateQuery() StakeStateType.StakeStateContext? GetStakeState(StateContext ctx, Address agentAddress) { var stakeStateAddress = StakeState.DeriveAddress(agentAddress); - if (ctx.AccountState.TryGetStakeStateV2(agentAddr: agentAddress, out StakeStateV2 stakeStateV2)) + if (ctx.WorldState.TryGetStakeStateV2(agentAddr: agentAddress, out StakeStateV2 stakeStateV2)) { return new StakeStateType.StakeStateContext( stakeStateV2, stakeStateAddress, - ctx.AccountState, + ctx.WorldState, ctx.BlockIndex, ctx.StateMemoryCache ); @@ -309,13 +310,12 @@ public StateQuery() resolve: context => { var agentAddress = context.GetArgument
("agentAddress"); - if (!(context.Source.GetState(agentAddress) is Dictionary value)) + if (!(context.Source.WorldState.GetAgentState(agentAddress) is { } agentState)) { return null; } - var agentState = new AgentState(value); var deriveAddress = MonsterCollectionState.DeriveAddress(agentAddress, agentState.MonsterCollectionRound); - if (context.Source.GetState(deriveAddress) is Dictionary state) + if (context.Source.WorldState.GetLegacyState(deriveAddress) is Dictionary state) { return new MonsterCollectionState(state); } @@ -330,13 +330,9 @@ public StateQuery() { var sheetAddress = Addresses.GetSheetAddress(); var rewardSheetAddress = Addresses.GetSheetAddress(); - IReadOnlyList values = context.Source.GetStates(new[] - { - sheetAddress, - rewardSheetAddress, - }); - if (values[0] is Text ss && - values[1] is Text srs) + IValue sheetValue = context.Source.WorldState.GetLegacyState(sheetAddress); + IValue rewardSheetValue = context.Source.WorldState.GetLegacyState(rewardSheetAddress); + if (sheetValue is Text ss && rewardSheetValue is Text srs) { var monsterCollectionSheet = new MonsterCollectionSheet(); monsterCollectionSheet.Set(ss); @@ -354,7 +350,8 @@ public StateQuery() description: "The latest stake rewards based on StakePolicySheet.", resolve: context => { - var stakePolicySheetStateValue = context.Source.GetState(Addresses.GetSheetAddress()); + var stakePolicySheetStateValue = + context.Source.WorldState.GetLegacyState(Addresses.GetSheetAddress()); var stakePolicySheet = new StakePolicySheet(); if (stakePolicySheetStateValue is not Text stakePolicySheetStateText) { @@ -363,13 +360,14 @@ public StateQuery() stakePolicySheet.Set(stakePolicySheetStateText); - IReadOnlyList values = context.Source.GetStates(new[] - { - Addresses.GetSheetAddress(stakePolicySheet["StakeRegularFixedRewardSheet"].Value), - Addresses.GetSheetAddress(stakePolicySheet["StakeRegularRewardSheet"].Value), - }); + IValue fixedRewardSheetValue = + context.Source.WorldState.GetLegacyState( + Addresses.GetSheetAddress(stakePolicySheet["StakeRegularFixedRewardSheet"].Value)); + IValue rewardSheetValue = + context.Source.WorldState.GetLegacyState( + Addresses.GetSheetAddress(stakePolicySheet["StakeRegularRewardSheet"].Value)); - if (!(values[0] is Text fsv && values[1] is Text sv)) + if (!(fixedRewardSheetValue is Text fsv && rewardSheetValue is Text sv)) { return null; } @@ -393,19 +391,18 @@ public StateQuery() if (context.Source.BlockIndex < StakeState.StakeRewardSheetV2Index) { stakeRegularRewardSheet = new StakeRegularRewardSheet(); - stakeRegularRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularRewardSheetCsv); + //stakeRegularRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularRewardSheetCsv); stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet(); - stakeRegularFixedRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularFixedRewardSheetCsv); + //stakeRegularFixedRewardSheet.Set(ClaimStakeReward8.V1.StakeRegularFixedRewardSheetCsv); } else { - IReadOnlyList values = context.Source.GetStates(new[] - { - Addresses.GetSheetAddress(), - Addresses.GetSheetAddress() - }); + IValue rewardSheetValue = context.Source.WorldState.GetLegacyState( + Addresses.GetSheetAddress()); + IValue fixedRewardSheetValue = context.Source.WorldState.GetLegacyState( + Addresses.GetSheetAddress()); - if (!(values[0] is Text sv && values[1] is Text fsv)) + if (!(rewardSheetValue is Text sv && fixedRewardSheetValue is Text fsv)) { return null; } @@ -424,7 +421,7 @@ public StateQuery() resolve: context => { var sheetAddress = Addresses.GetSheetAddress(); - IValue? sheetValue = context.Source.GetState(sheetAddress); + IValue? sheetValue = context.Source.WorldState.GetLegacyState(sheetAddress); if (sheetValue is Text sv) { var crystalMonsterCollectionMultiplierSheet = new CrystalMonsterCollectionMultiplierSheet(); @@ -447,8 +444,8 @@ public StateQuery() { var avatarAddress = context.GetArgument
("avatarAddress"); var address = avatarAddress.Derive("recipe_ids"); - IReadOnlyList values = context.Source.AccountState.GetStates(new[] { address }); - if (values[0] is List rawRecipeIds) + IValue value = context.Source.WorldState.GetLegacyState(address); + if (value is List rawRecipeIds) { return rawRecipeIds.ToList(StateExtensions.ToInteger); } @@ -469,8 +466,8 @@ public StateQuery() { var avatarAddress = context.GetArgument
("avatarAddress"); var address = avatarAddress.Derive("world_ids"); - IReadOnlyList values = context.Source.AccountState.GetStates(new[] { address }); - if (values[0] is List rawWorldIds) + IValue value = context.Source.WorldState.GetLegacyState(address); + if (value is List rawWorldIds) { return rawWorldIds.ToList(StateExtensions.ToInteger); } @@ -492,7 +489,7 @@ public StateQuery() resolve: context => { var raiderAddress = context.GetArgument
("raiderAddress"); - if (context.Source.GetState(raiderAddress) is List list) + if (context.Source.WorldState.GetLegacyState(raiderAddress) is List list) { return new RaiderState(list); } @@ -522,7 +519,7 @@ public StateQuery() var prev = context.GetArgument("prev"); var sheet = new WorldBossListSheet(); var address = Addresses.GetSheetAddress(); - if (context.Source.GetState(address) is Text text) + if (context.Source.WorldState.GetLegacyState(address) is Text text) { sheet.Set(text); } @@ -543,7 +540,7 @@ public StateQuery() resolve: context => { var bossAddress = context.GetArgument
("bossAddress"); - if (context.Source.GetState(bossAddress) is List list) + if (context.Source.WorldState.GetLegacyState(bossAddress) is List list) { return new WorldBossState(list); } @@ -562,7 +559,7 @@ public StateQuery() resolve: context => { var address = context.GetArgument
("worldBossKillRewardRecordAddress"); - if (context.Source.GetState(address) is List list) + if (context.Source.WorldState.GetLegacyState(address) is List list) { return new WorldBossKillRewardRecord(list); } @@ -586,7 +583,7 @@ public StateQuery() { var address = context.GetArgument
("address"); var currency = context.GetArgument("currency"); - return context.Source.GetBalance(address, currency); + return context.Source.WorldState.GetBalance(address, currency); } ); @@ -600,7 +597,7 @@ public StateQuery() resolve: context => { var address = context.GetArgument
("raiderListAddress"); - if (context.Source.GetState(address) is List list) + if (context.Source.WorldState.GetLegacyState(address) is List list) { return list.ToList(StateExtensions.ToAddress); } @@ -618,7 +615,7 @@ public StateQuery() { var avatarAddress = context.GetArgument
("avatarAddress"); var orderDigestListAddress = OrderDigestListState.DeriveAddress(avatarAddress); - if (context.Source.GetState(orderDigestListAddress) is Dictionary d) + if (context.Source.WorldState.GetLegacyState(orderDigestListAddress) is Dictionary d) { return new OrderDigestListState(d); } @@ -638,7 +635,7 @@ public StateQuery() Address? address = null; bool approved = false; int mead = 0; - if (context.Source.GetState(pledgeAddress) is List l) + if (context.Source.WorldState.GetLegacyState(pledgeAddress) is List l) { address = l[0].ToAddress(); approved = l[1].ToBoolean(); @@ -671,12 +668,12 @@ public StateQuery() var blockIndex = context.Source.BlockIndex; var currentAvatarAddr = context.GetArgument
("avatarAddress"); var filterBounds = context.GetArgument("filterBounds"); - var currentRoundData = context.Source.AccountState.GetSheet().GetRoundByBlockIndex(blockIndex); + var currentRoundData = context.Source.WorldState.GetSheet().GetRoundByBlockIndex(blockIndex); int playerScore = ArenaScore.ArenaScoreDefault; var cacheKey = $"{currentRoundData.ChampionshipId}_{currentRoundData.Round}"; List result = new(); var scoreAddr = ArenaScore.DeriveAddress(currentAvatarAddr, currentRoundData.ChampionshipId, currentRoundData.Round); - var scoreState = context.Source.GetState(scoreAddr); + var scoreState = context.Source.WorldState.GetLegacyState(scoreAddr); if (scoreState is List scores) { playerScore = (Integer)scores[1]; diff --git a/NineChronicles.Headless/GraphTypes/StateQueryFields/Garages.cs b/NineChronicles.Headless/GraphTypes/StateQueryFields/Garages.cs index 9cfdf8051..70a2f016f 100644 --- a/NineChronicles.Headless/GraphTypes/StateQueryFields/Garages.cs +++ b/NineChronicles.Headless/GraphTypes/StateQueryFields/Garages.cs @@ -10,7 +10,9 @@ using Libplanet.Explorer.GraphTypes; using Nekoyume; using Nekoyume.Model.Garages; +using Nekoyume.Module; using NineChronicles.Headless.GraphTypes.States; +using System; namespace NineChronicles.Headless.GraphTypes; @@ -66,7 +68,7 @@ private void RegisterGarages() throw new ExecutionError($"Invalid currency enum: {currencyEnum}"); } - var balance = context.Source.GetBalance(garageBalanceAddr, currency); + var balance = context.Source.WorldState.GetBalance(garageBalanceAddr, currency); garageBalances.Add(balance); } } @@ -79,7 +81,7 @@ private void RegisterGarages() throw new ExecutionError($"Invalid currency ticker: {currencyTicker}"); } - var balance = context.Source.GetBalance(garageBalanceAddr, currency); + var balance = context.Source.WorldState.GetBalance(garageBalanceAddr, currency); garageBalances.Add(balance); } } @@ -97,10 +99,12 @@ private void RegisterGarages() agentAddr, HashDigest.FromString(fungibleItemId))) .ToArray(); - fungibleItemGarages = context.Source.GetStates(fungibleItemGarageAddresses) + fungibleItemGarages = fungibleItemGarageAddresses + .Select(address => context.Source.WorldState.GetLegacyState(address)) .Select((value, i) => value is null or Null ? (fungibleItemIds[i], fungibleItemGarageAddresses[i], null) - : (fungibleItemIds[i], fungibleItemGarageAddresses[i], new FungibleItemGarage(value))); + : (fungibleItemIds[i], fungibleItemGarageAddresses[i], new FungibleItemGarage(value))) + .ToArray(); } return new GaragesType.Value( diff --git a/NineChronicles.Headless/GraphTypes/States/AgentStateType.cs b/NineChronicles.Headless/GraphTypes/States/AgentStateType.cs index 1e203b6f7..f0b18afa7 100644 --- a/NineChronicles.Headless/GraphTypes/States/AgentStateType.cs +++ b/NineChronicles.Headless/GraphTypes/States/AgentStateType.cs @@ -10,6 +10,7 @@ using Nekoyume.Helper; using Nekoyume.Model.Quest; using Nekoyume.Model.State; +using Nekoyume.Module; using NineChronicles.Headless.GraphTypes.States.Models; using static Lib9c.SerializeKeys; @@ -19,8 +20,8 @@ public class AgentStateType : ObjectGraphType { public class AgentStateContext : StateContext { - public AgentStateContext(AgentState agentState, IAccountState accountState, long blockIndex, StateMemoryCache stateMemoryCache) - : base(accountState, blockIndex, stateMemoryCache) + public AgentStateContext(AgentState agentState, IWorldState worldState, long blockIndex, StateMemoryCache stateMemoryCache) + : base(worldState, blockIndex, stateMemoryCache) { AgentState = agentState; } @@ -45,10 +46,10 @@ public AgentStateType() resolve: context => { IReadOnlyList
avatarAddresses = context.Source.GetAvatarAddresses(); - return context.Source.AccountState.GetAvatarStates(avatarAddresses).Select( + return avatarAddresses.Select(avatarAddress => context.Source.WorldState.GetAvatarState(avatarAddress)).Select( x => new AvatarStateType.AvatarStateContext( x, - context.Source.AccountState, + context.Source.WorldState, context.Source.BlockIndex, context.Source.StateMemoryCache)); }); @@ -58,10 +59,10 @@ public AgentStateType() resolve: context => { Currency currency = new GoldCurrencyState( - (Dictionary)context.Source.GetState(GoldCurrencyState.Address)! + (Dictionary)context.Source.WorldState.GetLegacyState(GoldCurrencyState.Address)! ).Currency; - return context.Source.GetBalance( + return context.Source.WorldState.GetBalance( context.Source.AgentAddress, currency ).GetQuantityString(true); @@ -80,7 +81,7 @@ public AgentStateType() context.Source.AgentAddress, context.Source.AgentState.MonsterCollectionRound ); - if (context.Source.GetState(monsterCollectionAddress) is { } state) + if (context.Source.WorldState.GetLegacyState(monsterCollectionAddress) is { } state) { return new MonsterCollectionState((Dictionary)state).Level; } @@ -93,43 +94,15 @@ public AgentStateType() resolve: context => { IReadOnlyList
avatarAddresses = context.Source.GetAvatarAddresses(); - var addresses = new Address[avatarAddresses.Count * 2]; - for (int i = 0; i < avatarAddresses.Count; i++) - { - addresses[i] = avatarAddresses[i].Derive(LegacyQuestListKey); - addresses[avatarAddresses.Count + i] = avatarAddresses[i]; - } - - IReadOnlyList values = context.Source.GetStates(addresses); - for (int i = 0; i < avatarAddresses.Count; i++) - { - if (values[i] is { } rawQuestList) - { - var questList = new QuestList((Dictionary)rawQuestList); - var traded = IsTradeQuestCompleted(questList); - if (traded) - { - return true; - } - } - else if (values[avatarAddresses.Count + i] is { } state) - { - var avatarState = new AvatarState((Dictionary)state); - var traded = IsTradeQuestCompleted(avatarState.questList); - if (traded) - { - return true; - } - } - } - - return false; + IEnumerable avatarStates = + avatarAddresses.Select(address => context.Source.WorldState.GetAvatarState(address)); + return avatarStates.Any(avatarState => IsTradeQuestCompleted(avatarState.questList)); } ); Field>( "crystal", description: "Current CRYSTAL.", - resolve: context => context.Source.GetBalance( + resolve: context => context.Source.WorldState.GetBalance( context.Source.AgentAddress, CrystalCalculator.CRYSTAL ).GetQuantityString(true)); @@ -142,7 +115,7 @@ public AgentStateType() Address? address = null; bool approved = false; int mead = 0; - if (context.Source.GetState(pledgeAddress) is List l) + if (context.Source.WorldState.GetLegacyState(pledgeAddress) is List l) { address = l[0].ToAddress(); approved = l[1].ToBoolean(); diff --git a/NineChronicles.Headless/GraphTypes/States/AvatarStateType.cs b/NineChronicles.Headless/GraphTypes/States/AvatarStateType.cs index fd16acd87..87ffcaae4 100644 --- a/NineChronicles.Headless/GraphTypes/States/AvatarStateType.cs +++ b/NineChronicles.Headless/GraphTypes/States/AvatarStateType.cs @@ -8,6 +8,7 @@ using Libplanet.Action.State; using Nekoyume.Action; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; using NineChronicles.Headless.GraphTypes.States.Models; using NineChronicles.Headless.GraphTypes.States.Models.World; @@ -21,8 +22,8 @@ public class AvatarStateType : ObjectGraphType { - if (!(context.Source.GetState(context.Source.AvatarState.agentAddress) is Dictionary dictionary)) + if (context.Source.WorldState.GetAgentState(context.Source.AvatarState.agentAddress) is not + { } agentState) { throw new InvalidOperationException(); } - var agentState = new AgentState(dictionary); return agentState.avatarAddresses .First(x => x.Value.Equals(context.Source.AvatarState.address)) .Key; @@ -118,11 +119,11 @@ public AvatarStateType() description: "Rune list of avatar", resolve: context => { - var runeSheet = context.Source.AccountState.GetSheet(); + var runeSheet = context.Source.WorldState.GetSheet(); var runeList = new List(); foreach (var rune in runeSheet) { - var runeState = context.Source.GetState( + var runeState = context.Source.WorldState.GetLegacyState( RuneState.DeriveAddress(context.Source.AvatarState.address, rune.Id) ); if (runeState is not null) @@ -144,7 +145,7 @@ public AvatarStateType() resolve: context => { var addresses = context.Source.AvatarState.combinationSlotAddresses; - return context.Source.AccountState.GetStates(addresses) + return context.Source.WorldState.GetLegacyStates(addresses) .OfType() .Select(x => new CombinationSlotState(x)); }); diff --git a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs index 9d82d3f4b..bea0c8f7a 100644 --- a/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs +++ b/NineChronicles.Headless/GraphTypes/States/StakeStateType.cs @@ -1,21 +1,14 @@ -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; -using NineChronicles.Headless.GraphTypes.States.Models.Item; -using NineChronicles.Headless.GraphTypes.States.Models.Mail; -using NineChronicles.Headless.GraphTypes.States.Models.Quest; -using Nekoyume.Blockchain.Policy; using Nekoyume; using Nekoyume.Model.Stake; +using Nekoyume.Module; using Nekoyume.TableData; using NineChronicles.Headless.GraphTypes.Abstractions; @@ -25,8 +18,8 @@ public class StakeStateType : ObjectGraphType { public class StakeStateContext : StateContext { - public StakeStateContext(StakeStateV2 stakeState, Address address, IAccountState accountState, long blockIndex, StateMemoryCache stateMemoryCache) - : base(accountState, blockIndex, stateMemoryCache) + public StakeStateContext(StakeStateV2 stakeState, Address address, IWorldState worldState, long blockIndex, StateMemoryCache stateMemoryCache) + : base(worldState, blockIndex, stateMemoryCache) { StakeState = stakeState; Address = address; @@ -45,9 +38,10 @@ public StakeStateType() Field>( "deposit", description: "The staked amount.", - resolve: context => context.Source.AccountState.GetBalance( + resolve: context => context.Source.WorldState.GetBalance( context.Source.Address, - new GoldCurrencyState((Dictionary)context.Source.GetState(GoldCurrencyState.Address)!).Currency) + new GoldCurrencyState( + (Dictionary)context.Source.WorldState.GetLegacyState(GoldCurrencyState.Address)!).Currency) .GetQuantityString(true)); Field>( "startedBlockIndex", @@ -79,7 +73,7 @@ public StakeStateType() return null; } - IReadOnlyList values = context.Source.GetStates(new[] + IReadOnlyList values = context.Source.WorldState.GetLegacyStates(new[] { Addresses.GetSheetAddress(contract.StakeRegularFixedRewardSheetTableName), Addresses.GetSheetAddress(contract.StakeRegularRewardSheetTableName), diff --git a/NineChronicles.Headless/GraphTypes/States/StateContext.cs b/NineChronicles.Headless/GraphTypes/States/StateContext.cs index 25853a11b..df6b63e22 100644 --- a/NineChronicles.Headless/GraphTypes/States/StateContext.cs +++ b/NineChronicles.Headless/GraphTypes/States/StateContext.cs @@ -12,18 +12,18 @@ namespace NineChronicles.Headless.GraphTypes.States public class StateContext { public StateContext( - IAccountState accountState, + IWorldState worldState, long blockIndex, StateMemoryCache stateMemoryCache) { - AccountState = accountState; + WorldState = worldState; BlockIndex = blockIndex; - CurrencyFactory = new CurrencyFactory(() => accountState); + CurrencyFactory = new CurrencyFactory(() => worldState); FungibleAssetValueFactory = new FungibleAssetValueFactory(CurrencyFactory); StateMemoryCache = stateMemoryCache; } - public IAccountState AccountState { get; } + public IWorldState WorldState { get; } public long BlockIndex { get; } @@ -31,12 +31,6 @@ public StateContext( public FungibleAssetValueFactory FungibleAssetValueFactory { get; } - public IValue? GetState(Address address) => AccountState.GetState(address); - - public IReadOnlyList GetStates(IReadOnlyList
addresses) => AccountState.GetStates(addresses); - - public FungibleAssetValue GetBalance(Address address, Currency currency) => AccountState.GetBalance(address, currency); - public StateMemoryCache StateMemoryCache { get; } } } diff --git a/NineChronicles.Headless/NineChroniclesNodeService.cs b/NineChronicles.Headless/NineChroniclesNodeService.cs index 181f14e6e..96dbec06d 100644 --- a/NineChronicles.Headless/NineChroniclesNodeService.cs +++ b/NineChronicles.Headless/NineChroniclesNodeService.cs @@ -14,7 +14,6 @@ using Libplanet.Headless.Hosting; using Libplanet.Net; using Libplanet.Store; -using Libplanet.Types.Blocks; using Microsoft.Extensions.Hosting; using Nekoyume.Blockchain; using Nekoyume.Blockchain.Policy; @@ -293,7 +292,7 @@ internal void ConfigureContext(StandaloneContext standaloneContext) standaloneContext.Store = Store; standaloneContext.Swarm = Swarm; standaloneContext.CurrencyFactory = - new CurrencyFactory(() => standaloneContext.BlockChain.GetAccountState(standaloneContext.BlockChain.Tip.Hash)); + new CurrencyFactory(() => standaloneContext.BlockChain.GetWorldState(standaloneContext.BlockChain.Tip.Hash)); standaloneContext.FungibleAssetValueFactory = new FungibleAssetValueFactory(standaloneContext.CurrencyFactory); BootstrapEnded.WaitAsync().ContinueWith((task) => diff --git a/NineChronicles.Headless/Utils/CurrencyFactory.cs b/NineChronicles.Headless/Utils/CurrencyFactory.cs index 2c2945bf4..af92ef1cf 100644 --- a/NineChronicles.Headless/Utils/CurrencyFactory.cs +++ b/NineChronicles.Headless/Utils/CurrencyFactory.cs @@ -5,20 +5,21 @@ using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Model.State; +using Nekoyume.Module; using NineChronicles.Headless.GraphTypes; namespace NineChronicles.Headless.Utils; public class CurrencyFactory { - private readonly Func _accountStateGetter; + private readonly Func _worldStateGetter; private Currency? _ncg; public CurrencyFactory( - Func accountStateGetter, + Func worldStateGetter, Currency? ncg = null) { - _accountStateGetter = accountStateGetter; + _worldStateGetter = worldStateGetter; _ncg = ncg; } @@ -52,7 +53,7 @@ public bool TryGetCurrency(string ticker, out Currency currency) return _ncg; } - var value = _accountStateGetter().GetState(Addresses.GoldCurrency); + var value = _worldStateGetter().GetLegacyState(Addresses.GoldCurrency); if (value is Dictionary goldCurrencyDict) { var goldCurrency = new GoldCurrencyState(goldCurrencyDict); diff --git a/NineChronicles.RPC.Shared b/NineChronicles.RPC.Shared index dea4eb519..7e44fbae3 160000 --- a/NineChronicles.RPC.Shared +++ b/NineChronicles.RPC.Shared @@ -1 +1 @@ -Subproject commit dea4eb5193adf8228206172d214626a1e433e56f +Subproject commit 7e44fbae3c2b689ed4579b0c3b6037581ce3e88f