From 6fe88e3cd4599585055e8f39dfbcd3203db4cfce Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 20 Oct 2023 16:50:12 +0900 Subject: [PATCH] fix: Clean up BlockChain API, update Explorer query --- Libplanet.Action/State/IBlockChainStates.cs | 200 +++++------- Libplanet.Action/State/KeyConverters.cs | 2 +- .../Queries/StateQueryTest.cs | 46 ++- Libplanet.Explorer/Queries/StateQuery.cs | 233 ++++++++++---- Libplanet.Tests/Action/ActionEvaluatorTest.cs | 27 +- .../Blockchain/BlockChainTest.Append.cs | 15 +- Libplanet/Blockchain/BlockChain.cs | 286 ++++++++++-------- 7 files changed, 467 insertions(+), 342 deletions(-) diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index 7030423f53c..716330dc652 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Common; @@ -19,17 +18,13 @@ namespace Libplanet.Action.State public interface IBlockChainStates { /// - /// Gets a state associated to specified . + /// Returns the in the + /// at . /// - /// The of the state to query. - /// The of the account to fetch - /// the state from. - /// The of the to fetch - /// the state from. - /// The state associated to specified . - /// An absent state is represented as . The returned value - /// must be the same as the single element when retrieved via - /// . + /// The of the to create + /// for which to create an . + /// + /// The at . /// /// Thrown when is not /// and one of the following is true. @@ -43,25 +38,74 @@ public interface IBlockChainStates /// /// /// - /// - /// For performance reasons, it is generally recommended to use - /// with a batch of es instead of iterating over this method. - /// - IValue? GetState(Address address, Address accountAddress, BlockHash? offset); + /// + IWorldState GetWorldState(BlockHash? offset); + + /// + /// Returns the in the 's state storage + /// with . + /// + /// The state root hash for which to create + /// an . + /// + /// The with . + /// + /// Thrown when no with + /// as its state root hash is found. + /// + /// + IWorldState GetWorldState(HashDigest? stateRootHash); + + /// + /// Returns the in the + /// at . + /// + /// The of + /// to be returned. + /// The of the to create + /// for which to create an . + /// + /// The at . + /// + /// Thrown when is not + /// and one of the following is true. + /// + /// + /// Corresponding is not found in the . + /// + /// + /// Corresponding is found but its state root is not found + /// in the . + /// + /// + /// + /// + IAccountState GetAccountState(Address address, BlockHash? offset); + + /// + /// Returns the in the + /// of root hash . + /// + /// The of the root hash + /// for which to create an . + /// + /// The of state root hash . + /// + /// + IAccountState GetAccountState(HashDigest? stateRootHash); /// - /// Gets multiple states associated to specified . + /// Gets a state associated to specified . /// - /// The es of the states to query. + /// The of the state to query. /// The of the account to fetch - /// the states from. + /// the state from. /// The of the to fetch - /// the states from. - /// The states associated to specified . - /// Associated values are ordered in the same way to the corresponding - /// . Absent states are represented as . - /// Hence, the returned is guaranteed to be of the same - /// length as with possible values. + /// the state from. + /// The state associated to specified . + /// An absent state is represented as . The returned value + /// must be the same as the single element when retrieved via + /// . /// /// Thrown when is not /// and one of the following is true. @@ -75,27 +119,22 @@ public interface IBlockChainStates /// /// /// - IReadOnlyList GetStates( - IReadOnlyList
addresses, - Address accountAddress, - BlockHash? offset); - + /// + /// For performance reasons, it is generally recommended to use + /// with a batch of es instead of iterating over this method. + /// + IValue? GetState(Address address, Address accountAddress, BlockHash? offset); /// - /// Gets multiple states associated to specified . + /// Gets a state associated to specified . /// - /// The es of the states to query. - /// The of the state root hash - /// of the to fetch from. - /// The states associated to specified . - /// Associated values are ordered in the same way to the corresponding - /// . Absent states are represented as . - /// Hence, the returned is guaranteed to be of the same - /// length as with possible values. + /// The of the state to query. + /// The of the root hash + /// for which to create an . + /// The state associated to specified . + /// An absent state is represented as . /// - IReadOnlyList GetStates( - IReadOnlyList
addresses, - HashDigest? stateRootHash); + IValue? GetState(Address address, HashDigest? stateRootHash); /// /// Gets 's balance for given in the @@ -150,82 +189,5 @@ FungibleAssetValue GetTotalSupply( /// cannot be created. /// ValidatorSet GetValidatorSet(BlockHash? offset); - - /// - /// Returns the in the - /// at . - /// - /// The of the to create - /// for which to create an . - /// - /// The at . - /// - /// Thrown when is not - /// and one of the following is true. - /// - /// - /// Corresponding is not found in the . - /// - /// - /// Corresponding is found but its state root is not found - /// in the . - /// - /// - /// - /// - IWorldState GetWorldState(BlockHash? offset); - - /// - /// Returns the in the 's state storage - /// with . - /// - /// The state root hash for which to create - /// an . - /// - /// The with as its state root hash. - /// - /// Thrown when no with - /// as its state root hash is found. - /// - /// - IWorldState GetWorldState(HashDigest? hash); - - /// - /// Returns the in the - /// at . - /// - /// The of - /// to be returned. - /// The of the to create - /// for which to create an . - /// - /// The at . - /// - /// Thrown when is not - /// and one of the following is true. - /// - /// - /// Corresponding is not found in the . - /// - /// - /// Corresponding is found but its state root is not found - /// in the . - /// - /// - /// - /// - IAccountState GetAccountState(Address address, BlockHash? offset); - - /// - /// Returns the in the - /// of root hash . - /// - /// The of the root hash - /// for which to create an . - /// - /// The of state root hash . - /// - /// - IAccountState GetAccountState(HashDigest? stateRootHash); } } diff --git a/Libplanet.Action/State/KeyConverters.cs b/Libplanet.Action/State/KeyConverters.cs index ddd087d5a38..e40fd454b69 100644 --- a/Libplanet.Action/State/KeyConverters.cs +++ b/Libplanet.Action/State/KeyConverters.cs @@ -7,7 +7,7 @@ namespace Libplanet.Action.State public static class KeyConverters { // "___" - internal static readonly KeyBytes ValidatorSetKey = + public static readonly KeyBytes ValidatorSetKey = new KeyBytes(new byte[] { _underScore, _underScore, _underScore }); private const byte _underScore = 95; // '_' diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index dc51ea04159..818c94ad3ce 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -81,15 +81,15 @@ public async Task AccountStates() } [Fact] - public async Task States() + public async Task State() { (IBlockChainStates, IBlockPolicy) source = ( new MockChainStates(), new BlockPolicy() ); ExecutionResult result = await ExecuteQueryAsync(@" { - states( - addresses: [""0x5003712B63baAB98094aD678EA2B24BcE445D076"", ""0x0000000000000000000000000000000000000000""], + state( + address: ""0x5003712B63baAB98094aD678EA2B24BcE445D076"", accountAddress: ""0x40837BFebC1b192600023a431400557EA5FDE51a"" offsetBlockHash: ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" @@ -100,9 +100,9 @@ public async Task States() ExecutionNode resultData = Assert.IsAssignableFrom(result.Data); IDictionary resultDict = Assert.IsAssignableFrom>(resultData!.ToValue()); - object[] states = - Assert.IsAssignableFrom(resultDict["states"]); - Assert.Equal(new[] { new byte[] { 110, }, null }, states); + object state = + Assert.IsAssignableFrom(resultDict["state"]); + Assert.Equal(new byte[] { 110, }, state); } [Fact] @@ -252,7 +252,7 @@ public async Task ThrowExecutionErrorIfViolateMutualExclusive() } [Fact] - public async Task StatesBySrh() + public async Task StateBySrh() { var currency = Currency.Uncapped("ABC", 2, minters: null); (IBlockChainStates, IBlockPolicy) source = ( @@ -260,8 +260,9 @@ public async Task StatesBySrh() ); ExecutionResult result = await ExecuteQueryAsync(@" { - states( - addresses: [""0x5003712B63baAB98094aD678EA2B24BcE445D076"", ""0x0000000000000000000000000000000000000000""], + state( + address: ""0x5003712B63baAB98094aD678EA2B24BcE445D076"", + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetStateRootHash: ""c33b27773104f75ac9df5b0533854108bd498fab31e5236b6f1e1f6404d5ef64"" ) @@ -271,9 +272,9 @@ public async Task StatesBySrh() ExecutionNode resultData = Assert.IsAssignableFrom(result.Data); IDictionary resultDict = Assert.IsAssignableFrom>(resultData!.ToValue()); - object[] states = - Assert.IsAssignableFrom(resultDict["states"]); - Assert.Equal(new[] { new byte[] { 110, }, null }, states); + object state = + Assert.IsAssignableFrom(resultDict["state"]); + Assert.Equal(new byte[] { 110, }, state); } [Fact] @@ -407,10 +408,8 @@ private class MockChainStates : IBlockChainStates { public IReadOnlyList GetStates( IReadOnlyList
addresses, - HashDigest? stateRootHash) - { - throw new System.NotImplementedException(); - } + HashDigest? stateRootHash) => + addresses.Select(addr => GetAccountState(stateRootHash).GetState(addr)).ToList().AsReadOnly(); public FungibleAssetValue GetBalance( Address address, Currency currency, BlockHash? offset) => @@ -423,15 +422,12 @@ public ValidatorSet GetValidatorSet(BlockHash? offset) => new MockAccount().GetValidatorSet(); - public IValue GetState(Address address, Address accountAddress, BlockHash? offset) + public IValue? GetState(Address address, Address accountAddress, BlockHash? offset) => new MockAccount().GetState(address); + public IWorldState GetWorldState(BlockHash? offset) - { - throw new System.NotImplementedException(); - } + => new MockWorld(); - public IReadOnlyList GetStates(IReadOnlyList
addresses, Address accountAddress, BlockHash? offset) - => new MockAccount().GetStates(addresses); public IAccountState GetAccountState(Address address, BlockHash? blockHash) => new MockAccount(); @@ -446,14 +442,14 @@ public ITrie GetTrie(HashDigest? hash) throw new System.NotImplementedException(); } - public IWorldState GetBlockWorldState(BlockHash? offset) - => new MockWorld(); - public IWorldState GetWorldState(HashDigest? hash) => new MockWorld(); public IAccountState GetAccountState(HashDigest? hash) => new MockAccount(); + + public IValue? GetState(Address address, HashDigest? hash) + => new MockAccount().GetState(address); } private class MockWorld : IWorld diff --git a/Libplanet.Explorer/Queries/StateQuery.cs b/Libplanet.Explorer/Queries/StateQuery.cs index c967a5a198a..6fd072b6e29 100644 --- a/Libplanet.Explorer/Queries/StateQuery.cs +++ b/Libplanet.Explorer/Queries/StateQuery.cs @@ -21,28 +21,31 @@ public StateQuery() Field>( "worldState", arguments: new QueryArguments( - new QueryArgument> { Name = "offsetBlockHash" } + new QueryArgument { Name = "offsetBlockHash" }, + new QueryArgument { Name = "offsetStateRootHash" } ), resolve: ResolveWorldState ); Field>( "accountState", arguments: new QueryArguments( - new QueryArgument> { Name = "accountAddress" }, - new QueryArgument> { Name = "offsetBlockHash" } + new QueryArgument { Name = "accountAddress" }, + new QueryArgument { Name = "offsetBlockHash" }, + new QueryArgument { Name = "offsetStateRootHash" }, + new QueryArgument { Name = "accountStateRootHash" } ), resolve: ResolveAccountState ); - Field>>( - "states", + Field( + "state", arguments: new QueryArguments( - new QueryArgument>>> - { Name = "addresses" }, + new QueryArgument> { Name = "address" }, new QueryArgument> { Name = "accountAddress" }, new QueryArgument { Name = "offsetBlockHash" }, - new QueryArgument { Name = "offsetStateRootHash" } + new QueryArgument { Name = "offsetStateRootHash" }, + new QueryArgument { Name = "accountStateRootHash" } ), - resolve: ResolveStates + resolve: ResolveState ); Field>( "balance", @@ -76,89 +79,193 @@ public StateQuery() private static object ResolveWorldState( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { - string offsetBlockHash = context.GetArgument("offsetBlockHash"); + string? offsetBlockHash = context.GetArgument("offsetBlockHash"); + HashDigest? offsetStateRootHash = context + .GetArgument?>("offsetStateRootHash"); - BlockHash offset; - try - { - offset = BlockHash.FromString(offsetBlockHash); - } - catch (Exception e) + switch (blockhash: offsetBlockHash, srh: offsetStateRootHash) { - throw new ExecutionError( - "offsetBlockHash must consist of hexadecimal digits.\n" + e.Message, - e - ); - } + case (blockhash: not null, srh: not null): + throw new ExecutionError( + "offsetBlockHash and offsetStateRootHash cannot be specified at the same time." + ); + case (blockhash: null, srh: null): + throw new ExecutionError( + "Either offsetBlockHash or offsetStateRootHash must be specified." + ); + case (blockhash: not null, _): + { + BlockHash offset; + try + { + offset = BlockHash.FromString(offsetBlockHash); + } + catch (Exception e) + { + throw new ExecutionError( + "offsetBlockHash must consist of hexadecimal digits.\n" + e.Message, + e + ); + } + + return context.Source.ChainStates.GetWorldState(offset); + } - return context.Source.ChainStates.GetWorldState(offset); + case (_, srh: not null): + return context.Source.ChainStates.GetWorldState(offsetStateRootHash); + } } private static object ResolveAccountState( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { - Address accountAddress = context.GetArgument
("accountAddress"); - string offsetBlockHash = context.GetArgument("offsetBlockHash"); + Address? accountAddress = context.GetArgument
("accountAddress"); + string? offsetBlockHash = context.GetArgument("offsetBlockHash"); + HashDigest? offsetStateRootHash = context + .GetArgument?>("offsetStateRootHash"); + HashDigest? accountStateRootHash = context + .GetArgument?>("accountStateRootHash"); - BlockHash offset; - try + if (accountStateRootHash is { } accountSrh) { - offset = BlockHash.FromString(offsetBlockHash); + if (accountAddress is not null + || offsetBlockHash is not null + || offsetStateRootHash is not null) + { + throw new ExecutionError( + "Neither accountAddress, offsetBlockHash nor offsetStateRootHash " + + "cannot be specified with the accountStateRootHash." + ); + } + + return context.Source.ChainStates.GetAccountState(accountSrh); } - catch (Exception e) + else { - throw new ExecutionError( - "offsetBlockHash must consist of hexadecimal digits.\n" + e.Message, - e - ); - } + if (accountAddress is { } accountAddr) + { + switch (blockhash: offsetBlockHash, offsetSrh: offsetStateRootHash) + { + case (blockhash: not null, offsetSrh: not null): + throw new ExecutionError( + "offsetBlockHash and offsetStateRootHash " + + "cannot be specified at the same time." + ); + case (blockhash: null, offsetSrh: null): + throw new ExecutionError( + "Either offsetBlockHash or offsetStateRootHash must be specified." + ); + case (blockhash: not null, _): + { + BlockHash offset; + try + { + offset = BlockHash.FromString(offsetBlockHash); + } + catch (Exception e) + { + throw new ExecutionError( + "offsetBlockHash must consist of hexadecimal digits.\n" + + e.Message, + e + ); + } + + return context.Source.ChainStates + .GetWorldState(offset).GetAccount(accountAddr); + } - return context.Source.ChainStates.GetAccountState(accountAddress, offset); + case (_, offsetSrh: not null): + return context.Source.ChainStates + .GetWorldState(offsetStateRootHash).GetAccount(accountAddr); + } + } + else + { + throw new ExecutionError( + "accountAddress have to be specified with offset." + ); + } + } } - private static object ResolveStates( + private static object? ResolveState( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { - Address[] addresses = context.GetArgument("addresses"); - Address accountAddress = context.GetArgument
("accountAddress"); + Address address = context.GetArgument
("address"); + Address? accountAddress = context.GetArgument
("accountAddress"); string? offsetBlockHash = context.GetArgument("offsetBlockHash"); HashDigest? offsetStateRootHash = context .GetArgument?>("offsetStateRootHash"); + HashDigest? accountStateRootHash = context + .GetArgument?>("accountStateRootHash"); - switch (blockhash: offsetBlockHash, srh: offsetStateRootHash) + if (accountStateRootHash is { } accountSrh) { - case (blockhash: not null, srh: not null): - throw new ExecutionError( - "offsetBlockHash and offsetStateRootHash cannot be specified at the same time." - ); - case (blockhash: null, srh: null): + if (accountAddress is not null + || offsetBlockHash is not null + || offsetStateRootHash is not null) + { throw new ExecutionError( - "Either offsetBlockHash or offsetStateRootHash must be specified." + "Neither accountAddress, offsetBlockHash nor offsetStateRootHash " + + "cannot be specified with the accountStateRootHash." ); - case (blockhash: not null, _): + } + + return context.Source.ChainStates + .GetAccountState(accountSrh) + .GetState(address); + } + else + { + if (accountAddress is { } accountAddr) { - BlockHash offset; - try - { - offset = BlockHash.FromString(offsetBlockHash); - } - catch (Exception e) + switch (blockhash: offsetBlockHash, offsetSrh: offsetStateRootHash) { - throw new ExecutionError( - "offsetBlockHash must consist of hexadecimal digits.\n" + e.Message, - e - ); - } + case (blockhash: not null, offsetSrh: not null): + throw new ExecutionError( + "offsetBlockHash and offsetStateRootHash " + + "cannot be specified at the same time." + ); + case (blockhash: null, offsetSrh: null): + throw new ExecutionError( + "Either offsetBlockHash or offsetStateRootHash must be specified." + ); + case (blockhash: not null, _): + { + BlockHash offset; + try + { + offset = BlockHash.FromString(offsetBlockHash); + } + catch (Exception e) + { + throw new ExecutionError( + "offsetBlockHash must consist of hexadecimal digits.\n" + + e.Message, + e + ); + } + + return context.Source.ChainStates + .GetWorldState(offset) + .GetAccount(accountAddr) + .GetState(address); + } - return context.Source.ChainStates.GetStates( - addresses, - accountAddress, - offset + case (_, offsetSrh: not null): + return context.Source.ChainStates + .GetWorldState(offsetStateRootHash) + .GetAccount(accountAddr) + .GetState(address); + } + } + else + { + throw new ExecutionError( + "accountAddress have to be specified with offset." ); } - - case (_, srh: not null): - return context.Source.ChainStates.GetStates(addresses, offsetStateRootHash); } } diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index 8f7fbad592e..44f6a09ac02 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -158,12 +158,15 @@ public void Evaluate() Assert.Single(evaluations); Assert.Null(evaluations.Single().Exception); Assert.Equal( - chain.GetState(action.SignerKey, ReservedAddresses.LegacyAccount), + chain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(action.SignerKey), (Text)address.ToHex()); Assert.Equal( - chain.GetState(action.MinerKey, ReservedAddresses.LegacyAccount), + chain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(action.MinerKey), (Text)miner.ToAddress().ToHex()); - var state = chain.GetState(action.BlockIndexKey, ReservedAddresses.LegacyAccount); + var state = chain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(action.BlockIndexKey); Assert.Equal((long)(Integer)state, blockIndex); } @@ -1047,11 +1050,14 @@ public void EvaluateActionAndCollectFee() Assert.Null(evaluations.Single().Exception); Assert.Equal( FungibleAssetValue.FromRawValue(foo, 9), - chain.GetAccountState(evaluations.Single().OutputState).GetBalance(address, foo)); + chain.GetWorldState(evaluations.Single().OutputState) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(address, foo)); Assert.Equal( FungibleAssetValue.FromRawValue(foo, 1), - chain.GetAccountState( - evaluations.Single().OutputState).GetBalance(miner.ToAddress(), foo)); + chain.GetWorldState(evaluations.Single().OutputState) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(miner.ToAddress(), foo)); } [Fact] @@ -1117,11 +1123,14 @@ public void EvaluateThrowingExceedGasLimit() evaluations.Single().Exception?.InnerException?.GetType()); Assert.Equal( FungibleAssetValue.FromRawValue(foo, 5), - chain.GetAccountState(evaluations.Single().OutputState).GetBalance(address, foo)); + chain.GetWorldState(evaluations.Single().OutputState) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(address, foo)); Assert.Equal( FungibleAssetValue.FromRawValue(foo, 5), - chain.GetAccountState( - evaluations.Single().OutputState).GetBalance(miner.ToAddress(), foo)); + chain.GetWorldState(evaluations.Single().OutputState) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(miner.ToAddress(), foo)); } [Fact] diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 57af325e63e..160ca8109c0 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -222,10 +222,12 @@ Func getTxExecution var txExecution1 = getTxExecution(block3.Hash, tx1Transfer.Id); _logger.Verbose(nameof(txExecution1) + " = {@TxExecution}", txExecution1); Assert.False(txExecution1.Fail); - var inputAccount1 = _blockChain.GetAccountState( - Assert.IsType>(txExecution1.InputState)); - var outputAccount1 = _blockChain.GetAccountState( - Assert.IsType>(txExecution1.OutputState)); + var inputAccount1 = _blockChain.GetWorldState( + Assert.IsType>(txExecution1.InputState)) + .GetAccount(ReservedAddresses.LegacyAccount); + var outputAccount1 = _blockChain.GetWorldState( + Assert.IsType>(txExecution1.OutputState)) + .GetAccount(ReservedAddresses.LegacyAccount); var accountDiff1 = AccountDiff.Create(inputAccount1, outputAccount1); Assert.Equal( @@ -264,8 +266,9 @@ Func getTxExecution var txExecution3 = getTxExecution(block3.Hash, tx3Transfer.Id); _logger.Verbose(nameof(txExecution3) + " = {@TxExecution}", txExecution3); Assert.False(txExecution3.Fail); - var outputAccount3 = _blockChain.GetAccountState( - Assert.IsType>(txExecution3.OutputState)); + var outputAccount3 = _blockChain.GetWorldState( + Assert.IsType>(txExecution3.OutputState)) + .GetAccount(ReservedAddresses.LegacyAccount); Assert.Equal( DumbAction.DumbCurrency * -35, outputAccount3.GetBalance(pk.ToAddress(), DumbAction.DumbCurrency)); diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 4515b1cdf51..b56b90a35a9 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -478,107 +478,28 @@ public Transaction GetTransaction(TxId txId) } /// - /// Gets the current state of given in the - /// . - /// - /// An to get the states of. - /// An to get the states from. - /// The current state of given . This can be - /// if has no value. - public IValue GetState(Address address, Address accountAddress) => - GetState(address, accountAddress, Tip.Hash); - - /// - public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => - GetAccountState(accountAddress, offset).GetState(address); - - /// - /// Gets multiple states associated to the specified . - /// - /// Addresses of states to query. - /// An to get the states from. - /// The states associated to the specified . - /// Associated values are ordered in the same way to the corresponding - /// . Absent states are represented as . - /// - public IReadOnlyList GetStates( - IReadOnlyList
addresses, Address accountAddress) => - GetStates(addresses, accountAddress, Tip.Hash); - -#pragma warning disable MEN002 - /// -#pragma warning restore MEN002 - public IReadOnlyList GetStates( - IReadOnlyList
addresses, - Address accountAddress, - BlockHash? offset) => - GetAccountState(accountAddress, offset).GetStates(addresses); - -#pragma warning disable MEN002 - /// -#pragma warning restore MEN002 - public IReadOnlyList GetStates( - IReadOnlyList
addresses, - HashDigest? stateRootHash) => - StateStore.GetStateRoot(stateRootHash).Get(addresses.Select(ToStateKey).ToList()); - - /// - /// Queries 's current balance of the - /// in the . - /// - /// The owner to query. - /// The currency type to query. - /// The 's current balance. - /// - public FungibleAssetValue GetBalance( - Address address, - Currency currency) => - GetBalance(address, currency, Tip.Hash); - - /// - public FungibleAssetValue GetBalance( - Address address, - Currency currency, - BlockHash? offset) => - GetAccountState(ReservedAddresses.LegacyAccount, offset).GetBalance(address, currency); - - /// - /// Gets the current total supply of a in the - /// . + /// Returns the state trie associated with + /// . /// - /// The currency type to query. - /// The total supply value of at - /// in . - public FungibleAssetValue GetTotalSupply(Currency currency) => - GetTotalSupply(currency, Tip.Hash); - - /// - public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - GetAccountState(ReservedAddresses.LegacyAccount, offset).GetTotalSupply(currency); - - public ValidatorSet GetValidatorSet() => GetValidatorSet(Tip.Hash); - - /// - public ValidatorSet GetValidatorSet(BlockHash? offset) => - GetAccountState(ReservedAddresses.LegacyAccount, offset).GetValidatorSet(); - - /// - public IAccountState GetAccountState(Address address, BlockHash? offset) => - GetWorldState(offset).GetAccount(address); - - /// - public IAccountState GetAccountState(HashDigest? stateRootHash) => - new AccountBaseState(GetTrie(stateRootHash)); - - public IWorldState GetWorldState() => GetWorldState(Tip.Hash); - - /// - public IWorldState GetWorldState(BlockHash? offset) => - new WorldBaseState(GetTrie(offset), StateStore); - - /// - public IWorldState GetWorldState(HashDigest? hash) => - new WorldBaseState(GetTrie(hash), StateStore); + /// The to look up in + /// the internally held . + /// An representing the state root associated with + /// . + /// Thrown when the found in + /// with given is not recorded. + /// + /// + /// An returned by this method is read-only. + /// + public ITrie GetTrie(HashDigest? hash) + { + ITrie trie = StateStore.GetStateRoot(hash); + return trie.Recorded + ? trie + : throw new ArgumentException( + $"Could not find state root {hash} in {nameof(IStateStore)}.", + nameof(hash)); + } /// /// Returns the state root associated with @@ -623,29 +544,156 @@ public ITrie GetTrie(BlockHash? offset) } /// - /// Returns the state trie associated with - /// . + /// Returns the from , with given + /// and . /// - /// The to look up in - /// the internally held . - /// An representing the state root associated with - /// . - /// Thrown when the found in - /// with given is not recorded. - /// - /// - /// An returned by this method is read-only. - /// - public ITrie GetTrie(HashDigest? hash) + /// where is stored. + /// + /// State root hash of reference . + /// + /// retrieved from . + public IValue GetValue( + KeyBytes keyBytes, + HashDigest? stateRootHash) + => StateStore.GetStateRoot(stateRootHash).Get(keyBytes); + + /// + /// Returns the from , with given + /// and . + /// + /// where is stored. + /// + /// State root hash of reference . + /// + /// retrieved from . + public IValue GetValue( + Address address, + HashDigest? stateRootHash) + => StateStore.GetStateRoot(stateRootHash).Get(ToStateKey(address)); + + /// + /// Returns the sub state root hash from , with given + /// and . + /// + /// where sub state root hash is stored. + /// + /// State root hash of reference . + /// + /// retrieved from + /// as sub state root hash. + /// Thrown if retrieved + /// cannot be cast as . + public HashDigest GetSubStateRootHash( + Address address, + HashDigest? stateRootHash) { - ITrie trie = StateStore.GetStateRoot(hash); - return trie.Recorded - ? trie - : throw new ArgumentException( - $"Could not find state root {hash} in {nameof(IStateStore)}.", - nameof(hash)); + try + { + IValue value = GetValue(address, stateRootHash); + return new HashDigest((Binary)value); + + throw new InvalidCastException($"IValue {value} cannot be cast as state root hash"); + } + finally + { + } } + /// + public IWorldState GetWorldState(HashDigest? stateRootHash) + => new WorldBaseState(GetTrie(stateRootHash), StateStore); + + /// + public IWorldState GetWorldState(BlockHash? offset) + => new WorldBaseState(GetTrie(offset), StateStore); + + /// + /// Gets the current world state in the . + /// + /// The current world state. + public IWorldState GetWorldState() => GetWorldState(Tip.Hash); + + /// + public IAccountState GetAccountState(Address address, BlockHash? offset) => + GetWorldState(offset).GetAccount(address); + + /// + /// Gets the current account state of given in the + /// . + /// + /// An to get the account states of. + /// The current account state of given . This can be + /// if has no value. + public IAccountState GetAccountState(Address address) => + GetWorldState().GetAccount(address); + + /// + public IAccountState GetAccountState(HashDigest? stateRootHash) => + new AccountBaseState(GetTrie(stateRootHash)); + + /// + public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => + GetAccountState(accountAddress, offset).GetState(address); + + /// + public IValue GetState(Address address, HashDigest? stateRootHash) => + GetAccountState(stateRootHash).GetState(address); + + /// + /// Gets the current state of given and + /// in the . + /// + /// An to get the states of. + /// An to get the states from. + /// The current state of given and + /// . This can be + /// if or has no value. + /// + public IValue GetState(Address address, Address accountAddress) => + GetState(address, accountAddress, Tip.Hash); + + /// + /// Queries 's current balance of the + /// in the . + /// + /// The owner to query. + /// The currency type to query. + /// The 's current balance. + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency) + => GetBalance(address, currency, Tip.Hash); + + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency, + BlockHash? offset) + => GetWorldState(offset) + .GetAccount(ReservedAddresses.LegacyAccount).GetBalance(address, currency); + + /// + /// Gets the current total supply of a in the + /// . + /// + /// The currency type to query. + /// The total supply value of at + /// in . + public FungibleAssetValue GetTotalSupply(Currency currency) + => GetTotalSupply(currency, Tip.Hash); + + /// + public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) + => GetWorldState(offset) + .GetAccount(ReservedAddresses.LegacyAccount).GetTotalSupply(currency); + + public ValidatorSet GetValidatorSet() => GetValidatorSet(Tip.Hash); + + /// + public ValidatorSet GetValidatorSet(BlockHash? offset) + => GetWorldState(offset).GetAccount(ReservedAddresses.LegacyAccount).GetValidatorSet(); + /// /// Queries the recorded for a successful or failed /// within a .