From ca074e75dd63fa48db49d4b63f40fa829d554d69 Mon Sep 17 00:00:00 2001 From: Chanhyuck Ko Date: Mon, 16 Oct 2023 15:26:01 +0900 Subject: [PATCH 01/21] refactor: remove BlockChainState --- Libplanet.Explorer.Executable/Program.cs | 2 - .../Blockchain/BlockChainTest.Append.cs | 2 - .../Blockchain/BlockChainTest.ProposeBlock.cs | 2 - .../BlockChainTest.ValidateNextBlock.cs | 2 - Libplanet.Tests/Blockchain/BlockChainTest.cs | 15 --- Libplanet.Tests/TestUtils.cs | 2 - Libplanet/Blockchain/BlockChain.cs | 86 +++++++++---- Libplanet/Blockchain/BlockChainStates.cs | 113 ------------------ 8 files changed, 63 insertions(+), 161 deletions(-) delete mode 100644 Libplanet/Blockchain/BlockChainStates.cs diff --git a/Libplanet.Explorer.Executable/Program.cs b/Libplanet.Explorer.Executable/Program.cs index 45e7367bfb8..e82c1442f35 100644 --- a/Libplanet.Explorer.Executable/Program.cs +++ b/Libplanet.Explorer.Executable/Program.cs @@ -216,7 +216,6 @@ If omitted (default) explorer only the local blockchain store.")] new DumbBlockPolicy(LoadBlockPolicy(options)); IStagePolicy stagePolicy = new VolatileStagePolicy(); - var blockChainStates = new BlockChainStates(store, stateStore); var blockChain = new BlockChain( policy, @@ -224,7 +223,6 @@ If omitted (default) explorer only the local blockchain store.")] store, stateStore, options.GetGenesisBlock(policy), - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, stateStore, diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 4ff7204fe4a..e4ace3c7e92 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -496,14 +496,12 @@ public void AppendValidatesBlock() { var policy = new NullBlockPolicy( new BlockPolicyViolationException(string.Empty)); - var blockChainStates = new BlockChainStates(_fx.Store, _fx.StateStore); var blockChain = new BlockChain( policy, new VolatileStagePolicy(), _fx.Store, _fx.StateStore, _fx.GenesisBlock, - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, _fx.StateStore, diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs index 8c892a46f3f..03aa45dbc77 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs @@ -448,7 +448,6 @@ public void ProposeBlockWithBlockAction() var blockAction = new DumbAction(address1, "foo"); var policy = new BlockPolicy(blockAction); - var blockChainStates = new BlockChainStates(_fx.Store, _fx.StateStore); var blockChain = new BlockChain( policy, @@ -456,7 +455,6 @@ public void ProposeBlockWithBlockAction() _fx.Store, _fx.StateStore, _fx.GenesisBlock, - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, _fx.StateStore, diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs index ce70e70b331..a2aa00fa8f6 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs @@ -216,14 +216,12 @@ public void ValidateNextBlockInvalidStateRootHash() new SetStatesAtBlock(default, (Text)"foo", 1), policy.BlockInterval ); - var blockChainStates = new BlockChainStates(store, stateStore); var chain2 = new BlockChain( policyWithBlockAction, new VolatileStagePolicy(), store, stateStore, genesisBlock, - blockChainStates, new ActionEvaluator( _ => policyWithBlockAction.BlockAction, stateStore, diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index fa2355a049d..83a6e56ce8a 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -128,14 +128,12 @@ public void CanonicalId() Assert.Equal(chain1.Id, _fx.Store.GetCanonicalChainId()); var policy = new BlockPolicy(new MinerReward(1)); - var blockChainStates = new BlockChainStates(_fx.Store, _fx.StateStore); var z = new BlockChain( policy, new VolatileStagePolicy(), _fx.Store, _fx.StateStore, _fx.GenesisBlock, - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, _fx.StateStore, @@ -178,7 +176,6 @@ public void ProcessActions() { var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); - var blockChainStates = new BlockChainStates(store, stateStore); var policy = new BlockPolicy(); var actionLoader = TypedActionLoader.Create( typeof(BaseAction).Assembly, typeof(BaseAction)); @@ -1106,14 +1103,12 @@ public void GetStateOnlyDrillsDownUntilRequestedAddressesAreFound() { var policy = new NullBlockPolicy(); var tracker = new StoreTracker(_fx.Store); - var blockChainStates = new BlockChainStates(tracker, _fx.StateStore); var chain = new BlockChain( policy, new VolatileStagePolicy(), tracker, _fx.StateStore, _fx.GenesisBlock, - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, _fx.StateStore, @@ -1160,14 +1155,12 @@ public void GetStateReturnsEarlyForNonexistentAccount() { var policy = new NullBlockPolicy(); var tracker = new StoreTracker(_fx.Store); - var blockChainStates = new BlockChainStates(tracker, _fx.StateStore); var chain = new BlockChain( policy, new VolatileStagePolicy(), tracker, _fx.StateStore, _fx.GenesisBlock, - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, _fx.StateStore, @@ -1235,14 +1228,12 @@ public void GetStateReturnsLatestStatesWhenMultipleAddresses() var privateKeys = Enumerable.Range(1, 10).Select(_ => new PrivateKey()).ToList(); var addresses = privateKeys.Select(AddressExtensions.ToAddress).ToList(); var policy = new NullBlockPolicy(); - var blockChainStates = new BlockChainStates(_fx.Store, _fx.StateStore); var chain = new BlockChain( policy, new VolatileStagePolicy(), _fx.Store, _fx.StateStore, _fx.GenesisBlock, - blockChainStates, new ActionEvaluator( _ => policy.BlockAction, _fx.StateStore, @@ -1673,7 +1664,6 @@ internal static (Address, Address[] Addresses, BlockChain Chain) IBlockPolicy blockPolicy = new NullBlockPolicy(); store = new StoreTracker(store); Guid chainId = Guid.NewGuid(); - var chainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( _ => blockPolicy.BlockAction, stateStore: stateStore, @@ -1689,7 +1679,6 @@ internal static (Address, Address[] Addresses, BlockChain Chain) stateStore, genesisBlock, renderers: renderer is null ? null : new[] { renderer }, - blockChainStates: chainStates, actionEvaluator: actionEvaluator); var privateKey = new PrivateKey(); Address signer = privateKey.ToAddress(); @@ -1896,8 +1885,6 @@ private void CreateWithGenesisBlock() ), }; var txs = systemTxs.Concat(customTxs).ToImmutableList(); - var blockChainStates = new BlockChainStates( - storeFixture.Store, storeFixture.StateStore); var actionEvaluator = new ActionEvaluator( _ => policy.BlockAction, storeFixture.StateStore, @@ -1937,7 +1924,6 @@ private void ConstructWithUnexpectedGenesisBlock() var stagePolicy = new VolatileStagePolicy(); IStore store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); - var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( _ => policy.BlockAction, stateStore, @@ -1961,7 +1947,6 @@ private void ConstructWithUnexpectedGenesisBlock() store, stateStore, genesisBlockB, - blockChainStates, actionEvaluator); }); } diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index 3c03fc7e686..303f2e34921 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -606,7 +606,6 @@ public static (BlockChain BlockChain, ActionEvaluator ActionEvaluator) timestamp: timestamp ?? DateTimeOffset.MinValue), }; - var blockChainStates = new BlockChainStates(store, stateStore); var actionEvaluator = new ActionEvaluator( _ => policy.BlockAction, stateStore: stateStore, @@ -644,7 +643,6 @@ public static (BlockChain BlockChain, ActionEvaluator ActionEvaluator) stateStore, genesisBlock, renderers: renderers ?? new[] { validator = new ValidatingActionRenderer() }, - blockChainStates: blockChainStates, actionEvaluator: actionEvaluator ); #pragma warning restore S1121 diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 1989564468c..e9a57a7d2cc 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -17,6 +17,7 @@ using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; +using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; @@ -34,7 +35,7 @@ namespace Libplanet.Blockchain /// /// In order to watch its state changes, implement interface /// and pass it to the - /// + /// /// constructor. /// /// @@ -51,7 +52,6 @@ public partial class BlockChain : IBlockChainStates internal readonly ReaderWriterLockSlim _rwlock; private readonly object _txLock; private readonly ILogger _logger; - private readonly IBlockChainStates _blockChainStates; /// /// All s in the @@ -81,8 +81,6 @@ public partial class BlockChain : IBlockChainStates /// it checks if the existing genesis block and this argument is the same. /// If the has no genesis block yet this argument will /// be used for that. - /// The implementation for - /// state lookup. /// The implementation to /// calculate next states when append new blocks. /// Listens state changes on the created chain. Listens nothing @@ -104,7 +102,6 @@ public BlockChain( IStore store, IStateStore stateStore, Block genesisBlock, - IBlockChainStates blockChainStates, IActionEvaluator actionEvaluator, IEnumerable renderers = null) #pragma warning disable SA1118 // The parameter spans multiple lines @@ -118,7 +115,6 @@ public BlockChain( $"Given {nameof(store)} does not have canonical chain id set.", nameof(store)), genesisBlock, - blockChainStates, actionEvaluator, renderers) { @@ -131,7 +127,6 @@ private BlockChain( IStateStore stateStore, Guid id, Block genesisBlock, - IBlockChainStates blockChainStates, IActionEvaluator actionEvaluator, IEnumerable renderers) { @@ -155,8 +150,6 @@ private BlockChain( Store = store; StateStore = stateStore; - _blockChainStates = blockChainStates; - _blocks = new BlockSet(store); Renderers = renderers is IEnumerable r ? r.ToImmutableArray() @@ -206,7 +199,7 @@ private BlockChain( /// /// Since this value is immutable, renderers cannot be registered after once a object is instantiated; use renderers option of - /// + /// /// constructor instead. /// #pragma warning restore MEN002 @@ -353,8 +346,7 @@ public static BlockChain Create( IStateStore stateStore, Block genesisBlock, IActionEvaluator actionEvaluator, - IEnumerable renderers = null, - IBlockChainStates blockChainStates = null) + IEnumerable renderers = null) #pragma warning restore SA1611 // The documentation for parameters are missing. { if (store is null) @@ -414,8 +406,6 @@ public static BlockChain Create( store.SetCanonicalChainId(id); - blockChainStates ??= new BlockChainStates(store, stateStore); - return new BlockChain( policy, stagePolicy, @@ -423,7 +413,6 @@ public static BlockChain Create( stateStore, id, genesisBlock, - blockChainStates, actionEvaluator, renderers); } @@ -499,7 +488,7 @@ public IValue GetState(Address address) => /// public IValue GetState(Address address, BlockHash? offset) => - _blockChainStates.GetState(address, offset); + GetAccountState(offset).GetState(address); /// /// Gets multiple states associated to the specified . @@ -516,7 +505,7 @@ public IReadOnlyList GetStates(IReadOnlyList
addresses) => public IReadOnlyList GetStates( IReadOnlyList
addresses, BlockHash? offset) => - _blockChainStates.GetStates(addresses, offset); + GetAccountState(offset).GetStates(addresses); /// /// Queries 's current balance of the @@ -536,7 +525,7 @@ public FungibleAssetValue GetBalance( Address address, Currency currency, BlockHash? offset) => - _blockChainStates.GetBalance(address, currency, offset); + GetAccountState(offset).GetBalance(address, currency); /// /// Gets the current total supply of a in the @@ -550,21 +539,21 @@ public FungibleAssetValue GetTotalSupply(Currency currency) => /// public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - _blockChainStates.GetTotalSupply(currency, offset); + GetAccountState(offset).GetTotalSupply(currency); public ValidatorSet GetValidatorSet() => GetValidatorSet(Tip.Hash); /// public ValidatorSet GetValidatorSet(BlockHash? offset) => - _blockChainStates.GetValidatorSet(offset); + GetAccountState(offset).GetValidatorSet(); /// public IAccountState GetAccountState(BlockHash? offset) => - _blockChainStates.GetAccountState(offset); + new AccountState(GetTrie(offset)); /// public IAccountState GetAccountState(HashDigest? hash) => - _blockChainStates.GetAccountState(hash); + new AccountState(GetTrie(hash)); /// /// Queries the recorded for a successful or failed @@ -829,7 +818,6 @@ public BlockChain Fork(BlockHash point, bool inheritRenderers = true) StateStore, forkedId, Genesis, - _blockChainStates, ActionEvaluator, renderers); Store.ForkBlockIndexes(Id, forkedId, point); @@ -1286,5 +1274,57 @@ internal void CleanupBlockCommitStore(long limit) } } } + + /// + /// Returns the state root associated with + /// . + /// + /// The to look up in + /// the internally held . + /// An representing the state root associated with + /// . + /// Thrown for one of the following reasons. + /// + /// + /// If is not and + /// cannot be found in . + /// + /// + /// If is not and + /// the state root hash associated with + /// cannot be found in . + /// + /// + /// + /// + /// An returned by this method is read-only. + /// + private ITrie GetTrie(BlockHash? offset) + { + if (!(offset is { } hash)) + { + return StateStore.GetStateRoot(null); + } + else if (Store.GetStateRootHash(hash) is { } stateRootHash) + { + return GetTrie(stateRootHash); + } + else + { + throw new ArgumentException( + $"Could not find block hash {hash} in {nameof(IStore)}.", + nameof(offset)); + } + } + + private 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)); + } } } diff --git a/Libplanet/Blockchain/BlockChainStates.cs b/Libplanet/Blockchain/BlockChainStates.cs deleted file mode 100644 index 69e2b05dbc5..00000000000 --- a/Libplanet/Blockchain/BlockChainStates.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Common; -using Libplanet.Crypto; -using Libplanet.Store; -using Libplanet.Store.Trie; -using Libplanet.Types.Assets; -using Libplanet.Types.Blocks; -using Libplanet.Types.Consensus; - -namespace Libplanet.Blockchain -{ - /// - /// A default implementation of interface. - /// - public class BlockChainStates : IBlockChainStates - { - private readonly IStore _store; - private readonly IStateStore _stateStore; - - public BlockChainStates(IStore store, IStateStore stateStore) - { - _store = store; - _stateStore = stateStore; - } - - /// - public IValue? GetState( - Address address, BlockHash? offset) => - GetAccountState(offset).GetState(address); - - /// - public IReadOnlyList GetStates( - IReadOnlyList
addresses, BlockHash? offset) => - GetAccountState(offset).GetStates(addresses); - - /// - public FungibleAssetValue GetBalance( - Address address, Currency currency, BlockHash? offset) => - GetAccountState(offset).GetBalance(address, currency); - - /// - public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - GetAccountState(offset).GetTotalSupply(currency); - - /// - public ValidatorSet GetValidatorSet(BlockHash? offset) => - GetAccountState(offset).GetValidatorSet(); - - /// - public IAccountState GetAccountState(BlockHash? offset) => - new AccountState(GetTrie(offset)); - - /// - public IAccountState GetAccountState(HashDigest? hash) => - new AccountState(GetTrie(hash)); - - /// - /// Returns the state root associated with - /// . - /// - /// The to look up in - /// the internally held . - /// An representing the state root associated with - /// . - /// Thrown for one of the following reasons. - /// - /// - /// If is not and - /// cannot be found in . - /// - /// - /// If is not and - /// the state root hash associated with - /// cannot be found in . - /// - /// - /// - /// - /// An returned by this method is read-only. - /// - private ITrie GetTrie(BlockHash? offset) - { - if (!(offset is { } hash)) - { - return _stateStore.GetStateRoot(null); - } - else if (_store.GetStateRootHash(hash) is { } stateRootHash) - { - return GetTrie(stateRootHash); - } - else - { - throw new ArgumentException( - $"Could not find block hash {hash} in {nameof(IStore)}.", - nameof(offset)); - } - } - - private 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)); - } - } -} From 978ffd213f3f60f1a88617ae24c645204d3044fc Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Sep 2023 14:44:11 +0900 Subject: [PATCH 02/21] feat: Introduce IWorld, fix Libplanet.Action.State Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit b3e50db7feaa4095a01268fa08356ec64d7316c2) --- Libplanet.Action/ActionEvaluator.cs | 5 +- Libplanet.Action/State/Account.cs | 2 +- .../{AccountState.cs => AccountBaseState.cs} | 4 +- Libplanet.Action/State/IAccountState.cs | 4 + Libplanet.Action/State/IBlockChainStates.cs | 92 ++++++++++++++++--- Libplanet.Action/State/IWorld.cs | 62 +++++++++++++ Libplanet.Action/State/IWorldDelta.cs | 24 +++++ Libplanet.Action/State/IWorldState.cs | 48 ++++++++++ Libplanet.Action/State/ReservedAddresses.cs | 16 ++++ Libplanet.Action/State/World.cs | 92 +++++++++++++++++++ Libplanet.Action/State/WorldBaseState.cs | 83 +++++++++++++++++ Libplanet.Action/State/WorldDelta.cs | 24 +++++ .../State/WorldDeltaExtensions.cs | 62 +++++++++++++ Libplanet.Action/State/WorldExtensions.cs | 25 +++++ .../Queries/StateQueryTest.cs | 14 +-- 15 files changed, 531 insertions(+), 26 deletions(-) rename Libplanet.Action/State/{AccountState.cs => AccountBaseState.cs} (96%) create mode 100644 Libplanet.Action/State/IWorld.cs create mode 100644 Libplanet.Action/State/IWorldDelta.cs create mode 100644 Libplanet.Action/State/IWorldState.cs create mode 100644 Libplanet.Action/State/ReservedAddresses.cs create mode 100644 Libplanet.Action/State/World.cs create mode 100644 Libplanet.Action/State/WorldBaseState.cs create mode 100644 Libplanet.Action/State/WorldDelta.cs create mode 100644 Libplanet.Action/State/WorldDeltaExtensions.cs create mode 100644 Libplanet.Action/State/WorldExtensions.cs diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index ffdfbae2e91..061845bc104 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -471,9 +471,10 @@ internal ActionEvaluation EvaluatePolicyBlockAction( actions: new[] { policyBlockAction }.ToImmutableList()).Single(); } - internal IAccount PrepareInitialDelta(HashDigest? stateRootHash) + internal IWorld PrepareInitialDelta(HashDigest? stateRootHash) { - return new Account(new AccountState(_stateStore.GetStateRoot(stateRootHash))); + return new World( + new WorldBaseState(_stateStore.GetStateRoot(stateRootHash), _stateStore)); } internal IReadOnlyList diff --git a/Libplanet.Action/State/Account.cs b/Libplanet.Action/State/Account.cs index b0c33ac0ff3..f2524d7425b 100644 --- a/Libplanet.Action/State/Account.cs +++ b/Libplanet.Action/State/Account.cs @@ -40,7 +40,7 @@ private Account( TotalUpdatedFungibles = totalUpdatedFungibles; } - /// + /// public ITrie Trie => _baseState.Trie; /// diff --git a/Libplanet.Action/State/AccountState.cs b/Libplanet.Action/State/AccountBaseState.cs similarity index 96% rename from Libplanet.Action/State/AccountState.cs rename to Libplanet.Action/State/AccountBaseState.cs index 9fbf1787610..b325f52f5b1 100644 --- a/Libplanet.Action/State/AccountState.cs +++ b/Libplanet.Action/State/AccountBaseState.cs @@ -12,12 +12,12 @@ namespace Libplanet.Action.State /// /// A default implementation of interface. /// - public class AccountState : IAccountState + public class AccountBaseState : IAccountState { private ITrie _trie; private AccountStateCache _cache; - public AccountState(ITrie trie) + public AccountBaseState(ITrie trie) { _trie = trie; _cache = new AccountStateCache(); diff --git a/Libplanet.Action/State/IAccountState.cs b/Libplanet.Action/State/IAccountState.cs index a82ce4967c4..f40bb455879 100644 --- a/Libplanet.Action/State/IAccountState.cs +++ b/Libplanet.Action/State/IAccountState.cs @@ -4,6 +4,7 @@ using Libplanet.Crypto; using Libplanet.Store.Trie; using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; namespace Libplanet.Action.State @@ -26,6 +27,9 @@ namespace Libplanet.Action.State ///
public interface IAccountState { + /// + /// The of the . + /// [Pure] public ITrie Trie { get; } diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index a33f010fa1a..fb3cc9a745d 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -22,8 +22,10 @@ public interface IBlockChainStates /// Gets a state associated to specified . ///
/// The of the state to query. + /// The of the account to fetch + /// the state from. /// The of the to fetch - /// the states from. + /// 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 @@ -45,18 +47,20 @@ 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, BlockHash? offset); + IValue? GetState(Address address, Address accountAddress, BlockHash? offset); /// /// Gets multiple states associated to specified . /// /// The es of the states to query. + /// The of the account to fetch + /// the states 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 guaranteeed to be of the same + /// Hence, the returned is guarenteeed to be of the same /// length as with possible values. /// /// Thrown when is not @@ -73,6 +77,7 @@ public interface IBlockChainStates /// IReadOnlyList GetStates( IReadOnlyList
addresses, + Address accountAddress, BlockHash? offset); /// @@ -129,10 +134,36 @@ FungibleAssetValue GetTotalSupply( /// 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 /// at . /// + /// The of + /// to be returned. /// The of the to create /// for which to create an . /// @@ -151,21 +182,52 @@ FungibleAssetValue GetTotalSupply( /// /// /// - IAccountState GetAccountState(BlockHash? offset); + IAccountState GetAccountState(Address address, BlockHash? offset); /// - /// Returns the in the 's state storage - /// with . + /// Returns the state trie associated 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. + /// The to look up in + /// the internally held . + /// An representing the state trie associated with + /// . + /// Thrown for one of the following reasons. + /// + /// + /// If is not and + /// cannot be found in . + /// + /// + /// If is not and + /// the state root hash associated with + /// cannot be found in . + /// + /// /// - /// - IAccountState GetAccountState(HashDigest? hash); + ITrie GetBlockTrie(BlockHash? offset); + + /// + /// Returns the state trie associated with + /// . + /// + /// The to look up in + /// the internally held . + /// An representing the state trie associated with + /// . + /// Thrown for one of the following reasons. + /// + /// + /// If is not and + /// cannot be found in . + /// + /// + /// If is not and + /// the state root hash associated with + /// cannot be found in . + /// + /// + /// + ITrie GetTrie(HashDigest? hash); } } diff --git a/Libplanet.Action/State/IWorld.cs b/Libplanet.Action/State/IWorld.cs new file mode 100644 index 00000000000..7cbd17b131f --- /dev/null +++ b/Libplanet.Action/State/IWorld.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + /// + /// An interface to manipulate an world state with + /// maintaining . + /// It is like a map which is virtually initialized such + /// that every possible exists and + /// is mapped to . That means that: + /// + /// + /// it does not have length, + /// + /// + /// its index getter never throws + /// , + /// but returns instead, and + /// + /// + /// filling an with + /// account cannot be distinguished from + /// the having never been set to + /// any account. + /// + /// + /// + /// + /// This interface is immutable. + /// method does not manipulate the instance, but returns a new + /// instance with updated states. + /// + public interface IWorld : IWorldState + { + /// + /// The representing the delta part of + /// this . + /// + [Pure] + IWorldDelta Delta { get; } + + /// + /// Gets a new instance that the world state of the given + /// is set to the given + /// . + /// + /// The of the account to set. + /// The new account to fill the account with. + /// A new instance that + /// the account state of the given + /// is set to the given . + /// + /// This method method does not manipulate the instance, + /// but returns a new instance + /// with updated world instead. + /// + [Pure] + IWorld SetAccount(Address address, IAccount account); + } +} diff --git a/Libplanet.Action/State/IWorldDelta.cs b/Libplanet.Action/State/IWorldDelta.cs new file mode 100644 index 00000000000..8bcaf4a9672 --- /dev/null +++ b/Libplanet.Action/State/IWorldDelta.cs @@ -0,0 +1,24 @@ +using System.Collections.Immutable; +using System.Diagnostics.Contracts; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + public interface IWorldDelta + { + /// + /// A dictionary representing changed account states for each . + /// + [Pure] + IImmutableDictionary Accounts { get; } + + /// + /// + /// A set of es where each has + /// its account changed. + /// + /// + [Pure] + IImmutableSet
UpdatedAddresses { get; } + } +} diff --git a/Libplanet.Action/State/IWorldState.cs b/Libplanet.Action/State/IWorldState.cs new file mode 100644 index 00000000000..d00ca89adf3 --- /dev/null +++ b/Libplanet.Action/State/IWorldState.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using Libplanet.Crypto; +using Libplanet.Types.Blocks; + +namespace Libplanet.Action.State +{ + /// + /// An interface to fetch world states. + /// It is like a readonly map which is virtually initialized such + /// that every possible exists and + /// is mapped to . That means that: + /// + /// + /// it does not have length, + /// + /// + /// its index getter never throws + /// , + /// but returns instead, and + /// + /// + /// filling an with + /// state cannot be distinguished from + /// the having never been set to + /// any state. + /// + /// + /// + public interface IWorldState + { + /// + /// Whether is in legacy state or not. + /// + bool Legacy { get; } + + /// + /// Gets the of the given . + /// + /// The referring + /// the world to get its state. + /// The of the given . + /// If it has never been set to any state it returns + /// instead. + [Pure] + IAccount GetAccount(Address address); + } +} diff --git a/Libplanet.Action/State/ReservedAddresses.cs b/Libplanet.Action/State/ReservedAddresses.cs new file mode 100644 index 00000000000..cd002b6033a --- /dev/null +++ b/Libplanet.Action/State/ReservedAddresses.cs @@ -0,0 +1,16 @@ +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + public static class ReservedAddresses + { + public static readonly Address LegacyAccount + = new Address("1000000000000000000000000000000000000000"); + + public static readonly Address FungibleAssetsAccount + = new Address("1000000000000000000000000000000000000001"); + + public static readonly Address ValidatorSetAddress + = new Address("1000000000000000000000000000000000000002"); + } +} diff --git a/Libplanet.Action/State/World.cs b/Libplanet.Action/State/World.cs new file mode 100644 index 00000000000..21334ff0fda --- /dev/null +++ b/Libplanet.Action/State/World.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + /// + /// An internal implementation of . + /// + [Pure] + public class World : IWorld + { + private readonly IWorldState _baseState; + + public World(IWorldState baseState) + : this(baseState, new WorldDelta()) + { + } + + public World(IWorldState baseState, IWorldDelta delta) + { + _baseState = baseState; + Delta = delta; + Legacy = true; + } + + /// + [Pure] + public IWorldDelta Delta { get; private set; } + + /// + [Pure] + public bool Legacy { get; private set; } + + /// + [Pure] + public IAccount GetAccount(Address address) + { + return Delta.Accounts.TryGetValue(address, out IAccount? account) + ? account! + : _baseState.GetAccount(address); + } + + /// + [Pure] + public IWorld SetAccount(Address address, IAccount account) + { + if (!address.Equals(ReservedAddresses.LegacyAccount) + && account.Delta.UpdatedFungibleAssets.Count > 0) + { + return this; + } + + return new World(this, new WorldDelta(Delta.Accounts.SetItem(address, account))) + { Legacy = Legacy && address.Equals(ReservedAddresses.LegacyAccount) }; + } + + /// + /// Creates a new World from given . + /// + /// The previous to initialize + /// new World. + /// A null world using as its base state. + /// + public static IWorld Create(IWorldState previousWorld) => + new World(previousWorld) + { Legacy = previousWorld.Legacy }; + + /// + /// Creates a null worlds from given . + /// + /// The previous to use. + /// A null world that is of the same type as . + /// + /// Thrown if given + /// is not . + /// + public static IWorld Flush(IWorld world) + { + foreach (KeyValuePair kv in world.Delta.Accounts) + { + world = world.SetAccount(kv.Key, Account.Flush(kv.Value)); + } + + return world is World impl + ? new World(impl) { Legacy = impl.Legacy } + : throw new ArgumentException( + $"Unknown type for {nameof(world)}: {world.GetType()}"); + } + } +} diff --git a/Libplanet.Action/State/WorldBaseState.cs b/Libplanet.Action/State/WorldBaseState.cs new file mode 100644 index 00000000000..200e5ca60d2 --- /dev/null +++ b/Libplanet.Action/State/WorldBaseState.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Common; +using Libplanet.Crypto; +using Libplanet.Store; +using Libplanet.Store.Trie; +using static Libplanet.Action.State.KeyConverters; + +namespace Libplanet.Action.State +{ + /// + /// A default implementation of interface. + /// It acts as root state of recursion. + /// + public class WorldBaseState : IWorldState + { + private readonly ITrie _trie; + private readonly IStateStore _stateStore; + + public WorldBaseState(ITrie trie, IStateStore stateStore) + { + _trie = trie; + _stateStore = stateStore; + Legacy = _trie + .Get(new[] + { + ToStateKey(ReservedAddresses.LegacyAccount), + }) + .Any(v => v == null); + } + + /// + public bool Legacy { get; } + + /// + public IAccount GetAccount(Address address) => GetAccounts(new[] { address }).First(); + + /// + /// Gets the s of the given . + /// + /// The es referring + /// the s to get its states. + /// The list of s of the given + /// . + public IReadOnlyList GetAccounts(IReadOnlyList
addresses) => + GetAccountStateRoot(addresses) + .Zip(addresses, (trie, address) => CreateAccount(trie)) + .ToList(); + + private IReadOnlyList GetAccountStateRoot(IReadOnlyList
addresses) + { + if (Legacy) + { + return addresses + .Select(GetLegacyTrieOnly) + .ToList(); + } + else + { + return _trie + .Get(addresses.Select(ToStateKey).ToList()) + .Select(GetTrieFromBencodex) + .ToList(); + } + } + + private ITrie GetLegacyTrieOnly(Address address) => + address == ReservedAddresses.LegacyAccount + ? _trie + : _stateStore.GetStateRoot(null); + + private ITrie GetTrieFromBencodex(IValue? value) => + value is Binary stateRootHash + ? _stateStore.GetStateRoot( + new HashDigest(stateRootHash.ToArray())) + : _stateStore.GetStateRoot(null); + + private IAccount CreateAccount(ITrie trie) => + new Account(new AccountBaseState(trie)); + } +} diff --git a/Libplanet.Action/State/WorldDelta.cs b/Libplanet.Action/State/WorldDelta.cs new file mode 100644 index 00000000000..da4b1e8e080 --- /dev/null +++ b/Libplanet.Action/State/WorldDelta.cs @@ -0,0 +1,24 @@ +using System.Collections.Immutable; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + internal class WorldDelta : IWorldDelta + { + internal WorldDelta() + { + Accounts = ImmutableDictionary.Empty; + } + + internal WorldDelta(IImmutableDictionary accounts) + { + Accounts = accounts; + } + + /// + public IImmutableSet
UpdatedAddresses => Accounts.Keys.ToImmutableHashSet(); + + /// + public IImmutableDictionary Accounts { get; } + } +} diff --git a/Libplanet.Action/State/WorldDeltaExtensions.cs b/Libplanet.Action/State/WorldDeltaExtensions.cs new file mode 100644 index 00000000000..6d27378d898 --- /dev/null +++ b/Libplanet.Action/State/WorldDeltaExtensions.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex.Types; +using Libplanet.Crypto; +using Libplanet.Store; +using Libplanet.Store.Trie; +using static Libplanet.Action.State.KeyConverters; + +namespace Libplanet.Action.State +{ + public static class WorldDeltaExtensions + { + /// + /// Aggregates a list of s in order. + /// + /// The list of s to aggregate. + /// The aggregate of as an + /// . + /// + /// + /// As aggregation is done by partially overwriting previous values, + /// the order in which is important. + /// + public static IWorldDelta OrderedSum(this IReadOnlyList deltas) + { + IImmutableDictionary accounts = deltas.Aggregate( + ImmutableDictionary.Empty, + (prev, next) => prev.SetItems(next.Accounts)); + return new WorldDelta(accounts); + } + + /// + /// Gets a raw dictionary representation of that gets + /// actually written to an . + /// + /// The to convert. + /// A raw dictionary representation of to write + /// to an . + public static IImmutableDictionary> + ToRawDelta(this IWorldDelta delta) + { + // NOTE: Is this key correct? + var rawStates = delta.Accounts.Select( + kv => new KeyValuePair>( + ToStateKey(kv.Key), + kv.Value.Delta.ToRawDelta())); + return ImmutableDictionary>.Empty + .SetItems(rawStates) + .ToImmutableDictionary(); + } + + public static IImmutableSet ToUpdatedStateKeys( + this IEnumerable deltas) + { + return deltas + .SelectMany(delta => delta.UpdatedAddresses) + .Select(address => ToStateKey(address)) + .ToImmutableHashSet(); + } + } +} diff --git a/Libplanet.Action/State/WorldExtensions.cs b/Libplanet.Action/State/WorldExtensions.cs new file mode 100644 index 00000000000..631c6bab00b --- /dev/null +++ b/Libplanet.Action/State/WorldExtensions.cs @@ -0,0 +1,25 @@ +using System.Collections.Immutable; +using Bencodex.Types; +using Libplanet.Crypto; + +namespace Libplanet.Action.State +{ + internal static class WorldExtensions + { + internal static IAccountState GetFungibleAssetsAccount(this IWorldState world) => + world.GetAccount(ReservedAddresses.LegacyAccount); + + internal static IAccount GetValidatorSetAccount(this IWorldState world) => + world.GetAccount(ReservedAddresses.LegacyAccount); + + internal static IWorld SetFungibleAssetsAccount( + this IWorld world, + IAccount account) => + world.SetAccount(ReservedAddresses.LegacyAccount, account); + + internal static IWorld SetValidatorSetAccount( + this IWorld world, + IAccount account) => + world.SetAccount(ReservedAddresses.LegacyAccount, account); + } +} diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index 2a8d24bd448..32bcbd83f16 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -174,7 +174,7 @@ public async Task Validators() Assert.Equal("032038e153d344773986c039ba5dbff12ae70cfdf6ea8beb7c5ea9b361a72a9233", validatorDict["publicKey"]); Assert.Equal(new BigInteger(1), validatorDict["power"]); } - + [Fact] public async Task ThrowExecutionErrorIfViolateMutualExclusive() { @@ -194,7 +194,7 @@ public async Task ThrowExecutionErrorIfViolateMutualExclusive() ", source: source); Assert.IsType(result.Errors); } - + [Fact] public async Task StatesBySrh() { @@ -345,7 +345,7 @@ public async Task ValidatorsBySrh() Assert.Equal("032038e153d344773986c039ba5dbff12ae70cfdf6ea8beb7c5ea9b361a72a9233", validatorDict["publicKey"]); Assert.Equal(new BigInteger(1), validatorDict["power"]); } - + private class MockChainStates : IBlockChainStates { @@ -366,7 +366,7 @@ public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) = public ValidatorSet GetValidatorSet(BlockHash? offset) => GetAccountState(offset).GetValidatorSet(); - public IAccountState GetAccountState(BlockHash? offset) => + public IAccountState GetAccountState(BlockHash? offset) => new MockAccount(offset, null); public IAccountState GetAccountState(HashDigest? hash) => @@ -380,12 +380,14 @@ public ITrie GetTrie(BlockHash? offset) private class MockAccount : IAccount { - public MockAccount(BlockHash? blockHash, HashDigest? stateRootHash) + public MockAccount(Address? address, HashDigest? stateRootHash) { - BlockHash = blockHash ?? default; + Address = address ?? default; StateRootHash = stateRootHash ?? default; } + public Address Address { get; } + public ITrie Trie { get; } public IAccountDelta Delta { get; } From b5d36fb205a600ba888890c9dc5ff332d7865c80 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Sep 2023 15:11:06 +0900 Subject: [PATCH 03/21] feat: Update ActionEvaluator Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit fa1b19fc5dc87b19f8b0b11ddbf1ee2cbec0bd1a) --- Libplanet.Action/ActionContext.cs | 4 +- Libplanet.Action/ActionEvaluation.cs | 4 +- .../ActionEvaluationsExtensions.cs | 18 ++++++++- Libplanet.Action/ActionEvaluator.cs | 22 +++++------ Libplanet.Action/FeeCollector.cs | 37 ++++++++++--------- Libplanet.Action/IAction.cs | 4 +- Libplanet.Action/IActionContext.cs | 4 +- Libplanet.Action/IActionEvaluation.cs | 2 +- Libplanet.Action/IFeeCollector.cs | 12 +++--- 9 files changed, 61 insertions(+), 46 deletions(-) diff --git a/Libplanet.Action/ActionContext.cs b/Libplanet.Action/ActionContext.cs index 179c42bcf30..f6cb0903329 100644 --- a/Libplanet.Action/ActionContext.cs +++ b/Libplanet.Action/ActionContext.cs @@ -19,7 +19,7 @@ public ActionContext( Address miner, long blockIndex, int blockProtocolVersion, - IAccount previousState, + IWorld previousState, int randomSeed, long gasLimit, bool rehearsal = false) @@ -56,7 +56,7 @@ public ActionContext( public bool Rehearsal { get; } /// - public IAccount PreviousState { get; } + public IWorld PreviousState { get; } /// public int RandomSeed { get; } diff --git a/Libplanet.Action/ActionEvaluation.cs b/Libplanet.Action/ActionEvaluation.cs index 4d3134c48eb..23171343197 100644 --- a/Libplanet.Action/ActionEvaluation.cs +++ b/Libplanet.Action/ActionEvaluation.cs @@ -24,7 +24,7 @@ public class ActionEvaluation : IActionEvaluation public ActionEvaluation( IAction action, IActionContext inputContext, - IAccount outputState, + IWorld outputState, Exception? exception = null) { Action = action; @@ -51,7 +51,7 @@ public ActionEvaluation( /// /// The result states that makes. /// - public IAccount OutputState { get; } + public IWorld OutputState { get; } /// /// An exception that had risen during evaluation. diff --git a/Libplanet.Action/ActionEvaluationsExtensions.cs b/Libplanet.Action/ActionEvaluationsExtensions.cs index dd22945a697..730f241d182 100644 --- a/Libplanet.Action/ActionEvaluationsExtensions.cs +++ b/Libplanet.Action/ActionEvaluationsExtensions.cs @@ -9,8 +9,9 @@ namespace Libplanet.Action { public static class ActionEvaluationsExtensions { - public static IImmutableDictionary GetRawTotalDelta( - this IReadOnlyList actionEvaluations) + public static IImmutableDictionary> + GetRawTotalDelta( + this IReadOnlyList actionEvaluations) { return actionEvaluations .Select(eval => eval.OutputState.Delta) @@ -18,5 +19,18 @@ public static IImmutableDictionary GetRawTotalDelta( .OrderedSum() .ToRawDelta(); } + + public static IImmutableDictionary + GetLegacyRawTotalDelta( + this IEnumerable actionEvaluations) + { + return actionEvaluations + .SelectMany(eval => eval.OutputState.Delta.Accounts) + .Where(kv => kv.Key.Equals(ReservedAddresses.LegacyAccount)) + .Select(kv => kv.Value.Delta) + .ToList() + .OrderedSum() + .ToRawDelta(); + } } } diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 061845bc104..1f6226ea381 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -91,7 +91,7 @@ public IReadOnlyList Evaluate( stopwatch.Start(); try { - IAccount previousState = PrepareInitialDelta(baseStateRootHash); + IWorld previousState = PrepareInitialDelta(baseStateRootHash); ImmutableList evaluations = EvaluateBlock(block, previousState).ToImmutableList(); @@ -165,12 +165,12 @@ public IReadOnlyList Evaluate( internal static IEnumerable EvaluateActions( IPreEvaluationBlockHeader blockHeader, ITransaction? tx, - IAccount previousState, + IWorld previousState, IImmutableList actions, ILogger? logger = null) { IActionContext CreateActionContext( - IAccount prevState, + IWorld prevState, int randomSeed, long actionGasLimit) { @@ -197,7 +197,7 @@ IActionContext CreateActionContext( byte[] preEvaluationHashBytes = blockHeader.PreEvaluationHash.ToByteArray(); int seed = GenerateRandomSeed(preEvaluationHashBytes, hashedSignature, signature, 0); - IAccount state = previousState; + IWorld state = previousState; foreach (IAction action in actions) { IActionContext context = CreateActionContext(state, seed, gasLimit); @@ -228,11 +228,11 @@ internal static (ActionEvaluation Evaluation, long NextGasLimit) EvaluateAction( ILogger? logger = null) { IActionContext inputContext = context; - IAccount state = inputContext.PreviousState; + IWorld state = inputContext.PreviousState; Exception? exc = null; IFeeCollector feeCollector = new FeeCollector(context, tx?.MaxGasPrice); - IActionContext CreateActionContext(IAccount newPrevState) + IActionContext CreateActionContext(IWorld newPrevState) { return new ActionContext( signer: inputContext.Signer, @@ -373,9 +373,9 @@ internal static IEnumerable OrderTxsForEvaluation( [Pure] internal IEnumerable EvaluateBlock( IPreEvaluationBlock block, - IAccount previousState) + IWorld previousState) { - IAccount delta = previousState; + IWorld delta = previousState; IEnumerable orderedTxs = OrderTxsForEvaluation( block.ProtocolVersion, block.Transactions, @@ -385,7 +385,7 @@ internal IEnumerable EvaluateBlock( { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); - delta = Account.Flush(delta); + delta = World.Flush(delta); IEnumerable evaluations = EvaluateTx( blockHeader: block, @@ -423,7 +423,7 @@ internal IEnumerable EvaluateBlock( internal IEnumerable EvaluateTx( IPreEvaluationBlockHeader blockHeader, ITransaction tx, - IAccount previousState) + IWorld previousState) { ImmutableList actions = ImmutableList.CreateRange(LoadActions(blockHeader.Index, tx)); @@ -449,7 +449,7 @@ internal IEnumerable EvaluateTx( [Pure] internal ActionEvaluation EvaluatePolicyBlockAction( IPreEvaluationBlockHeader blockHeader, - IAccount previousState) + IWorld previousState) { var policyBlockAction = _policyBlockActionGetter(blockHeader); if (policyBlockAction is null) diff --git a/Libplanet.Action/FeeCollector.cs b/Libplanet.Action/FeeCollector.cs index fc92e897ab5..d4a6c62b720 100644 --- a/Libplanet.Action/FeeCollector.cs +++ b/Libplanet.Action/FeeCollector.cs @@ -57,20 +57,21 @@ private FeeCollector( _state = state; } - public IAccount Mortgage(IAccount state) + public IWorld Mortgage(IWorld world) { if (_state == FeeCollectState.CannotCollectible || !CheckState(FeeCollectState.Mortgage)) { - return state; + return world; } if (!(_gasPrice is { } realGasPrice)) { - return state; + return world; } - var balance = state.GetBalance(_context.Signer, realGasPrice.Currency); + IAccountState accountState = world.GetFungibleAssetsAccountState(); + var balance = accountState.GetBalance(_context.Signer, realGasPrice.Currency); if (balance < realGasPrice * _context.GasLimit()) { var msg = @@ -80,66 +81,66 @@ public IAccount Mortgage(IAccount state) throw new InsufficientBalanceException(msg, _context.Signer, balance); } - IAccount nextState = state.BurnAsset( + IAccount nextAccount = new Account(accountState).BurnAsset( _context, _context.Signer, realGasPrice * _context.GasLimit()); _state = FeeCollectState.Mortgage; - return nextState; + return world.SetFungibleAssetsAccount(nextAccount); } - public IAccount Refund(IAccount state) + public IWorld Refund(IWorld world) { if (_state == FeeCollectState.CannotCollectible || !CheckState(FeeCollectState.Refund)) { - return state; + return world; } if (!(_gasPrice is { } realGasPrice) || realGasPrice.Sign <= 0) { - return state; + return world; } if (_context.GasLimit() - _context.GasUsed() <= 0) { _state = FeeCollectState.Refund; - return state; + return world; } - IAccount nextState = state.MintAsset( + IAccount nextAccount = new Account(world.GetFungibleAssetsAccountState()).MintAsset( _context, _context.Signer, (_context.GasLimit() - _context.GasUsed()) * realGasPrice); _state = FeeCollectState.Refund; - return nextState; + return world.SetFungibleAssetsAccount(nextAccount); } - public IAccount Reward(IAccount state) + public IWorld Reward(IWorld world) { if (_state == FeeCollectState.CannotCollectible || !CheckState(FeeCollectState.Reward)) { - return state; + return world; } if (!(_gasPrice is { } realGasPrice) || realGasPrice.Sign <= 0) { - return state; + return world; } if (_context.GasUsed() <= 0) { _state = FeeCollectState.Refund; - return state; + return world; } - IAccount nextState = state.MintAsset( + IAccount nextAccount = new Account(world.GetFungibleAssetsAccountState()).MintAsset( _context, _context.Miner, realGasPrice * _context.GasUsed()); _state = FeeCollectState.Reward; - return nextState; + return world.SetFungibleAssetsAccount(nextAccount); } public IFeeCollector Next(IActionContext context) diff --git a/Libplanet.Action/IAction.cs b/Libplanet.Action/IAction.cs index a81bacfbd4c..ae1a7eddda4 100644 --- a/Libplanet.Action/IAction.cs +++ b/Libplanet.Action/IAction.cs @@ -182,7 +182,7 @@ public interface IAction /// and a deterministic random seed. /// Other “bound” information resides in the action /// object in itself, as its properties (or fields). - /// A returned object functions as + /// A returned object functions as /// a delta which shifts from previous states to next states. /// /// A context object containing addresses that @@ -232,6 +232,6 @@ public interface IAction /// your project as a dependency. /// /// - IAccount Execute(IActionContext context); + IWorld Execute(IActionContext context); } } diff --git a/Libplanet.Action/IActionContext.cs b/Libplanet.Action/IActionContext.cs index 793ea3c2d78..673918e2a02 100644 --- a/Libplanet.Action/IActionContext.cs +++ b/Libplanet.Action/IActionContext.cs @@ -65,12 +65,12 @@ public interface IActionContext /// Although a instance is /// immutable, it has several manipulative methods that returns /// new instances with some "dirty" - /// states. These kinds of dirty + /// states. These kinds of dirty /// instances can be returned by method. ///
[Pure] - IAccount PreviousState { get; } + IWorld PreviousState { get; } /// /// The random seed to use for pseudorandom number generator. This value diff --git a/Libplanet.Action/IActionEvaluation.cs b/Libplanet.Action/IActionEvaluation.cs index 7ca29f0e3bb..3bce211a07c 100644 --- a/Libplanet.Action/IActionEvaluation.cs +++ b/Libplanet.Action/IActionEvaluation.cs @@ -24,7 +24,7 @@ public interface IActionEvaluation /// /// The result states that makes. /// - public IAccount OutputState { get; } + public IWorld OutputState { get; } /// /// An exception that had risen during evaluation. diff --git a/Libplanet.Action/IFeeCollector.cs b/Libplanet.Action/IFeeCollector.cs index 220d4002e08..3728598f655 100644 --- a/Libplanet.Action/IFeeCollector.cs +++ b/Libplanet.Action/IFeeCollector.cs @@ -16,31 +16,31 @@ public interface IFeeCollector /// /// Mortgage the fee from the sender of the transaction. /// - /// The state of the account. + /// The state of the account. /// The state of the account after the fee is mortgaged. - IAccount Mortgage(IAccount state); + IWorld Mortgage(IWorld world); /// /// Refund the fee to the sender of the transaction. /// - /// + /// /// The state of the account after the action is executed. /// /// /// The state of the account after the fee is refunded. /// - IAccount Refund(IAccount state); + IWorld Refund(IWorld world); /// /// Reward the proposer of the block. /// - /// + /// /// The state of the account after the fee is refunded. /// /// /// The state of the account after the proposer is rewarded. /// - IAccount Reward(IAccount state); + IWorld Reward(IWorld world); /// /// Change the action context and return the next fee collector. From 33ab70ba4a2d3ad5cd1e887e8783040f469a5db6 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Sep 2023 15:17:29 +0900 Subject: [PATCH 04/21] feat: Update actions Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit cebeb428a9558b5e7b5e6bbaab27e3442cf7c0d6) --- Libplanet.Action/NullAction.cs | 2 +- Libplanet.Action/Sys/Initialize.cs | 46 +++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/Libplanet.Action/NullAction.cs b/Libplanet.Action/NullAction.cs index f58f652cf21..e980d41adf1 100644 --- a/Libplanet.Action/NullAction.cs +++ b/Libplanet.Action/NullAction.cs @@ -25,7 +25,7 @@ public void LoadPlainValue(IValue plainValue) PlainValue = plainValue; } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { return context.PreviousState; } diff --git a/Libplanet.Action/Sys/Initialize.cs b/Libplanet.Action/Sys/Initialize.cs index 621facc4ac5..128ee5376e1 100644 --- a/Libplanet.Action/Sys/Initialize.cs +++ b/Libplanet.Action/Sys/Initialize.cs @@ -18,7 +18,7 @@ public sealed class Initialize : IAction { public Initialize( ValidatorSet validatorSet, - IImmutableDictionary states + IImmutableDictionary> states ) { ValidatorSet = validatorSet; @@ -30,7 +30,11 @@ public Initialize() // Used only for deserialization. See also class Libplanet.Action.Sys.Registry. } - public IImmutableDictionary? States { get; private set; } + public IImmutableDictionary>? States + { + get; + private set; + } public ValidatorSet? ValidatorSet { get; private set; } @@ -47,7 +51,12 @@ public IValue PlainValue s.Select( kv => new KeyValuePair( (Binary)kv.Key.Bencoded, - kv.Value + new Dictionary(kv.Value.Select( + kv2 => new KeyValuePair( + (Binary)kv2.Key.Bencoded, + kv2.Value + ) + )) ) ) ) @@ -59,8 +68,10 @@ public IValue PlainValue } } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { + IWorld world = context.PreviousState; + if (context.BlockIndex != 0) { throw new InvalidOperationException( @@ -68,24 +79,33 @@ public IAccount Execute(IActionContext context) ); } - IAccount states = context.PreviousState; if (ValidatorSet is { } vs) { + IAccount account = world.GetValidatorSetAccountState(); + foreach (Validator v in vs.Validators) { - states = states.SetValidator(v); + account = account.SetValidator(v); } + + world = world.SetAccount(account); } if (States is { } s) { - foreach (KeyValuePair kv in s) + foreach (KeyValuePair> kv in s) { - states = states.SetState(kv.Key, kv.Value); + var acc = world.GetAccount(kv.Key); + foreach (KeyValuePair kv2 in kv.Value) + { + acc = acc.SetState(kv2.Key, kv2.Value); + } + + world = world.SetAccount(acc); } } - return states; + return world; } public void LoadPlainValue(IValue plainValue) @@ -128,7 +148,13 @@ public void LoadPlainValue(IValue plainValue) ValidatorSet = new ValidatorSet((List)valuesList[0]); States = ((Dictionary)valuesList[1]) - .Select(kv => new KeyValuePair(new Address(kv.Key), kv.Value)) + .Select(kv => new KeyValuePair>( + new Address(kv.Key), + ((Dictionary)kv.Value) + .Select(kv2 => new KeyValuePair( + new Address(kv2.Key), + kv2.Value)) + .ToImmutableDictionary())) .ToImmutableDictionary(); } } From cd2fcf814d2b8e0ffe136a407168092e1c59ee60 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Sep 2023 16:57:11 +0900 Subject: [PATCH 05/21] feat: Update BlockChain Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit 4a1a6161df76a02cec69934e84e6a0b706e5f952) --- Libplanet.Action/ActionEvaluator.cs | 165 ++++++++++++++++-- Libplanet.Action/FeeCollector.cs | 10 +- Libplanet.Action/State/IBlockChainStates.cs | 24 ++- Libplanet.Action/State/IWorldState.cs | 7 +- Libplanet.Action/State/KeyConverters.cs | 10 +- Libplanet.Action/State/World.cs | 5 + Libplanet.Action/State/WorldBaseState.cs | 12 +- Libplanet.Action/State/WorldExtensions.cs | 7 +- Libplanet.Action/Sys/Initialize.cs | 6 +- .../Queries/StateQueryTest.cs | 32 ++-- Libplanet/Blockchain/BlockChain.Evaluate.cs | 3 +- .../Blockchain/BlockChain.TxExecution.cs | 3 +- Libplanet/Blockchain/BlockChain.cs | 118 ++++++------- 13 files changed, 277 insertions(+), 125 deletions(-) diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 1f6226ea381..39123113482 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -49,6 +49,9 @@ public ActionEvaluator( _actionLoader = actionTypeLoader; } + private delegate (ITrie, int) StateCommitter( + ITrie worldTrie, IActionEvaluation evaluation); + /// [Pure] public IActionLoader ActionLoader => _actionLoader; @@ -486,20 +489,42 @@ internal IReadOnlyList Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); - ITrie trie = _stateStore.GetStateRoot(baseStateRootHash); + ITrie worldTrie = _stateStore.GetStateRoot(baseStateRootHash); + IWorldState previousBlockWorld = new WorldBaseState(worldTrie, _stateStore); + IWorldState world = previousBlockWorld; + + IWorldState nextWorld; + ITrie nextWorldTrie; + var committedEvaluations = new List(); int setCount = 0; + int subCount; foreach (var evaluation in evaluations) { - ITrie nextTrie = trie; - foreach (var kv in evaluation.OutputState.Delta.ToRawDelta()) + nextWorld = evaluation.OutputState; + StateCommitter committer; + + if (world.Legacy && nextWorld.Legacy) { - nextTrie = nextTrie.Set(kv.Key, kv.Value); - setCount++; + committer = CommitLegacyState; } + else if (world.Legacy && !nextWorld.Legacy) + { + committer = CommitLegacyToModernState; + } + else if (!world.Legacy && !nextWorld.Legacy) + { + committer = CommitModernState; + } + else + { + throw new ApplicationException("World cannot be mutated from modern to legacy"); + } + + (nextWorldTrie, subCount) = committer(worldTrie, evaluation); + setCount += subCount; - nextTrie = _stateStore.Commit(nextTrie); var committedEvaluation = new CommittedActionEvaluation( action: evaluation.Action, inputContext: new CommittedActionContext( @@ -509,14 +534,15 @@ internal IReadOnlyList blockIndex: evaluation.InputContext.BlockIndex, blockProtocolVersion: evaluation.InputContext.BlockProtocolVersion, rehearsal: evaluation.InputContext.Rehearsal, - previousState: trie.Hash, + previousState: worldTrie.Hash, randomSeed: evaluation.InputContext.RandomSeed, blockAction: evaluation.InputContext.BlockAction), - outputState: nextTrie.Hash, + outputState: nextWorldTrie.Hash, exception: evaluation.Exception); committedEvaluations.Add(committedEvaluation); - trie = nextTrie; + world = nextWorld; + worldTrie = nextWorldTrie; } _logger @@ -528,13 +554,132 @@ internal IReadOnlyList "block #{BlockIndex} pre-evaluation hash {PreEvaluationHash}", stopwatch.ElapsedMilliseconds, setCount, - trie.Hash, + worldTrie.Hash, block.Index, block.PreEvaluationHash); return committedEvaluations; } + internal (ITrie, int) CommitLegacyState( + ITrie worldTrie, IActionEvaluation evaluation) + { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + + var totalDelta = evaluation.OutputState.GetAccount( + ReservedAddresses.LegacyAccount).Delta.ToRawDelta(); + + int setCount = 0; + + foreach (var kv in totalDelta) + { + worldTrie = worldTrie.Set(kv.Key, kv.Value); + setCount++; + } + + worldTrie = _stateStore.Commit(worldTrie); + + _logger + .ForContext("Tag", "Metric") + .ForContext("Subtag", "CommitDuration") + .Information( + "Took {DurationMs} ms to commit the trie with {KeyCount} key changes " + + "and resulting in state root hash {StateRootHash} ", + stopwatch.ElapsedMilliseconds, + setCount, + worldTrie.Hash); + + return (worldTrie, setCount); + } + + internal (ITrie, int) CommitModernState( + ITrie worldTrie, IActionEvaluation evaluation) + { + Stopwatch stopwatch = new Stopwatch(); + + stopwatch.Start(); + + IImmutableDictionary> + accountSubStateDelta = evaluation.OutputState.Delta.ToRawDelta(); + + IImmutableDictionary?> + accountSubStateRoot = GetAccountSubStateRootHashes(worldTrie, evaluation); + + int setCount = 0; + + foreach (var kv in accountSubStateDelta) + { + ITrie accountTrie = _stateStore.GetStateRoot(accountSubStateRoot[kv.Key]); + var accountDelta = kv.Value; + + foreach (KeyValuePair pair in accountDelta) + { + accountTrie = accountTrie.Set(pair.Key, pair.Value); + setCount++; + } + + accountTrie = _stateStore.Commit(accountTrie); + worldTrie = worldTrie.Set(kv.Key, new Binary(accountTrie.Hash.ToByteArray())); + } + + worldTrie = _stateStore.Commit(worldTrie); + + _logger + .ForContext("Tag", "Metric") + .ForContext("Subtag", "CommitDuration") + .Information( + "Took {DurationMs} ms to commit the trie with {KeyCount} key changes " + + "and resulting in state root hash {StateRootHash} ", + stopwatch.ElapsedMilliseconds, + setCount, + worldTrie); + + return (worldTrie, setCount); + } + + internal (ITrie, int) CommitLegacyToModernState( + ITrie worldTrie, IActionEvaluation evaluation) + { + Stopwatch stopwatch = new Stopwatch(); + worldTrie = _stateStore.GetStateRoot(null).Set( + KeyConverters.ToStateKey(ReservedAddresses.LegacyAccount), + new Binary( + worldTrie.Hash.ToByteArray())); + + _logger + .ForContext("Tag", "Metric") + .ForContext("Subtag", "LegacyCommitDuration") + .Information( + "Took {DurationMs} ms to commit the legacy trie " + + "and resulting in state root hash {StateRootHash} ", + stopwatch.ElapsedMilliseconds, + worldTrie.Hash); + + return CommitModernState(worldTrie, evaluation); + } + + internal IImmutableDictionary?> + GetAccountSubStateRootHashes( + ITrie worldTrie, IActionEvaluation evaluation) + { + var result = new Dictionary?>(); + foreach (var updatedAddress in evaluation.OutputState.Delta.UpdatedAddresses) + { + var key = KeyConverters.ToStateKey(updatedAddress); + var iValue = worldTrie.Get(new KeyBytes[] { key }).First(); + HashDigest? hash = null; + if (iValue is IValue value) + { + hash = new HashDigest(((Binary?)value)?.ToByteArray()); + } + + result.Add(key, hash); + } + + return result.ToImmutableDictionary(); + } + [Pure] private static IEnumerable OrderTxsForEvaluationV0( IEnumerable txs, diff --git a/Libplanet.Action/FeeCollector.cs b/Libplanet.Action/FeeCollector.cs index d4a6c62b720..bd1ce2971f9 100644 --- a/Libplanet.Action/FeeCollector.cs +++ b/Libplanet.Action/FeeCollector.cs @@ -70,8 +70,8 @@ public IWorld Mortgage(IWorld world) return world; } - IAccountState accountState = world.GetFungibleAssetsAccountState(); - var balance = accountState.GetBalance(_context.Signer, realGasPrice.Currency); + IAccount account = world.GetFungibleAssetsAccount(); + var balance = account.GetBalance(_context.Signer, realGasPrice.Currency); if (balance < realGasPrice * _context.GasLimit()) { var msg = @@ -81,7 +81,7 @@ public IWorld Mortgage(IWorld world) throw new InsufficientBalanceException(msg, _context.Signer, balance); } - IAccount nextAccount = new Account(accountState).BurnAsset( + IAccount nextAccount = new Account(account).BurnAsset( _context, _context.Signer, realGasPrice * _context.GasLimit()); @@ -108,7 +108,7 @@ public IWorld Refund(IWorld world) return world; } - IAccount nextAccount = new Account(world.GetFungibleAssetsAccountState()).MintAsset( + IAccount nextAccount = world.GetFungibleAssetsAccount().MintAsset( _context, _context.Signer, (_context.GasLimit() - _context.GasUsed()) * realGasPrice); @@ -135,7 +135,7 @@ public IWorld Reward(IWorld world) return world; } - IAccount nextAccount = new Account(world.GetFungibleAssetsAccountState()).MintAsset( + IAccount nextAccount = world.GetFungibleAssetsAccount().MintAsset( _context, _context.Miner, realGasPrice * _context.GasUsed()); diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index fb3cc9a745d..aa680991e8d 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -185,12 +185,12 @@ FungibleAssetValue GetTotalSupply( IAccountState GetAccountState(Address address, BlockHash? offset); /// - /// Returns the state trie associated with + /// Returns the state root associated with /// . /// /// The to look up in /// the internally held . - /// An representing the state trie associated with + /// An representing the state root associated with /// . /// Thrown for one of the following reasons. /// @@ -205,7 +205,10 @@ FungibleAssetValue GetTotalSupply( /// /// /// - ITrie GetBlockTrie(BlockHash? offset); + /// + /// An returned by this method is read-only. + /// + ITrie GetTrie(BlockHash? offset); /// /// Returns the state trie associated with @@ -213,21 +216,24 @@ FungibleAssetValue GetTotalSupply( /// /// The to look up in /// the internally held . - /// An representing the state trie associated with - /// . + /// An representing the state root associated with + /// . /// Thrown for one of the following reasons. /// /// - /// If is not and - /// cannot be found in . + /// If is not and + /// cannot be found in . /// /// - /// If is not and - /// the state root hash associated with + /// If is not and + /// the state root hash associated with /// cannot be found in . /// /// /// + /// + /// An returned by this method is read-only. + /// ITrie GetTrie(HashDigest? hash); } } diff --git a/Libplanet.Action/State/IWorldState.cs b/Libplanet.Action/State/IWorldState.cs index d00ca89adf3..b3b7f3250fc 100644 --- a/Libplanet.Action/State/IWorldState.cs +++ b/Libplanet.Action/State/IWorldState.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using Libplanet.Crypto; -using Libplanet.Types.Blocks; +using Libplanet.Store.Trie; namespace Libplanet.Action.State { @@ -29,6 +29,11 @@ namespace Libplanet.Action.State /// public interface IWorldState { + /// + /// The of the . + /// + public ITrie Trie { get; } + /// /// Whether is in legacy state or not. /// diff --git a/Libplanet.Action/State/KeyConverters.cs b/Libplanet.Action/State/KeyConverters.cs index d4ac98c11dc..ddd087d5a38 100644 --- a/Libplanet.Action/State/KeyConverters.cs +++ b/Libplanet.Action/State/KeyConverters.cs @@ -4,7 +4,7 @@ namespace Libplanet.Action.State { - internal static class KeyConverters + public static class KeyConverters { // "___" internal static readonly KeyBytes ValidatorSetKey = @@ -33,7 +33,7 @@ internal static class KeyConverters }; // $"{ByteUtil.Hex(address.ByteArray)}" - internal static KeyBytes ToStateKey(Address address) + public static KeyBytes ToStateKey(Address address) { var addressBytes = address.ByteArray; byte[] buffer = new byte[addressBytes.Length * 2]; @@ -47,7 +47,7 @@ internal static KeyBytes ToStateKey(Address address) } // $"_{ByteUtil.Hex(address.ByteArray)}_{ByteUtil.Hex(currency.Hash.ByteArray)}" - internal static KeyBytes ToFungibleAssetKey(Address address, Currency currency) + public static KeyBytes ToFungibleAssetKey(Address address, Currency currency) { var addressBytes = address.ByteArray; var currencyBytes = currency.Hash.ByteArray; @@ -71,12 +71,12 @@ internal static KeyBytes ToFungibleAssetKey(Address address, Currency currency) return new KeyBytes(buffer); } - internal static KeyBytes ToFungibleAssetKey( + public static KeyBytes ToFungibleAssetKey( (Address Address, Currency Currency) pair) => ToFungibleAssetKey(pair.Address, pair.Currency); // $"__{ByteUtil.Hex(currency.Hash.ByteArray)}" - internal static KeyBytes ToTotalSupplyKey(Currency currency) + public static KeyBytes ToTotalSupplyKey(Currency currency) { var currencyBytes = currency.Hash.ByteArray; byte[] buffer = new byte[currencyBytes.Length * 2 + 2]; diff --git a/Libplanet.Action/State/World.cs b/Libplanet.Action/State/World.cs index 21334ff0fda..cb46f789601 100644 --- a/Libplanet.Action/State/World.cs +++ b/Libplanet.Action/State/World.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using Libplanet.Crypto; +using Libplanet.Store.Trie; namespace Libplanet.Action.State { @@ -29,6 +30,10 @@ public World(IWorldState baseState, IWorldDelta delta) [Pure] public IWorldDelta Delta { get; private set; } + /// + [Pure] + public ITrie Trie => _baseState.Trie; + /// [Pure] public bool Legacy { get; private set; } diff --git a/Libplanet.Action/State/WorldBaseState.cs b/Libplanet.Action/State/WorldBaseState.cs index 200e5ca60d2..5f39a49f109 100644 --- a/Libplanet.Action/State/WorldBaseState.cs +++ b/Libplanet.Action/State/WorldBaseState.cs @@ -16,14 +16,13 @@ namespace Libplanet.Action.State /// public class WorldBaseState : IWorldState { - private readonly ITrie _trie; private readonly IStateStore _stateStore; public WorldBaseState(ITrie trie, IStateStore stateStore) { - _trie = trie; + Trie = trie; _stateStore = stateStore; - Legacy = _trie + Legacy = Trie .Get(new[] { ToStateKey(ReservedAddresses.LegacyAccount), @@ -31,6 +30,9 @@ public WorldBaseState(ITrie trie, IStateStore stateStore) .Any(v => v == null); } + /// + public ITrie Trie { get; } + /// public bool Legacy { get; } @@ -59,7 +61,7 @@ private IReadOnlyList GetAccountStateRoot(IReadOnlyList
addresse } else { - return _trie + return Trie .Get(addresses.Select(ToStateKey).ToList()) .Select(GetTrieFromBencodex) .ToList(); @@ -68,7 +70,7 @@ private IReadOnlyList GetAccountStateRoot(IReadOnlyList
addresse private ITrie GetLegacyTrieOnly(Address address) => address == ReservedAddresses.LegacyAccount - ? _trie + ? Trie : _stateStore.GetStateRoot(null); private ITrie GetTrieFromBencodex(IValue? value) => diff --git a/Libplanet.Action/State/WorldExtensions.cs b/Libplanet.Action/State/WorldExtensions.cs index 631c6bab00b..4235700fc56 100644 --- a/Libplanet.Action/State/WorldExtensions.cs +++ b/Libplanet.Action/State/WorldExtensions.cs @@ -6,7 +6,7 @@ namespace Libplanet.Action.State { internal static class WorldExtensions { - internal static IAccountState GetFungibleAssetsAccount(this IWorldState world) => + internal static IAccount GetFungibleAssetsAccount(this IWorldState world) => world.GetAccount(ReservedAddresses.LegacyAccount); internal static IAccount GetValidatorSetAccount(this IWorldState world) => @@ -16,10 +16,5 @@ internal static IWorld SetFungibleAssetsAccount( this IWorld world, IAccount account) => world.SetAccount(ReservedAddresses.LegacyAccount, account); - - internal static IWorld SetValidatorSetAccount( - this IWorld world, - IAccount account) => - world.SetAccount(ReservedAddresses.LegacyAccount, account); } } diff --git a/Libplanet.Action/Sys/Initialize.cs b/Libplanet.Action/Sys/Initialize.cs index 128ee5376e1..f2021a165e9 100644 --- a/Libplanet.Action/Sys/Initialize.cs +++ b/Libplanet.Action/Sys/Initialize.cs @@ -81,14 +81,14 @@ public IWorld Execute(IActionContext context) if (ValidatorSet is { } vs) { - IAccount account = world.GetValidatorSetAccountState(); + IAccount account = world.GetValidatorSetAccount(); foreach (Validator v in vs.Validators) { account = account.SetValidator(v); } - world = world.SetAccount(account); + world = world.SetAccount(ReservedAddresses.LegacyAccount, account); } if (States is { } s) @@ -101,7 +101,7 @@ public IWorld Execute(IActionContext context) acc = acc.SetState(kv2.Key, kv2.Value); } - world = world.SetAccount(acc); + world = world.SetAccount(kv.Key, acc); } } diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index 32bcbd83f16..f2aa3674e5b 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -349,25 +349,32 @@ public async Task ValidatorsBySrh() private class MockChainStates : IBlockChainStates { - public IValue GetState(Address address, BlockHash? offset) => - GetAccountState(offset).GetState(address); + public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => + GetAccountState(accountAddress, offset).GetState(address); public IReadOnlyList GetStates( - IReadOnlyList
addresses, BlockHash? offset) => - GetAccountState(offset).GetStates(addresses); + IReadOnlyList
addresses, Address accountAddress, BlockHash? offset) => + GetAccountState(accountAddress, offset).GetStates(addresses); public FungibleAssetValue GetBalance( - Address address, Currency currency, BlockHash? offset) => - GetAccountState(offset).GetBalance(address, currency); + Address address, + Currency currency, + BlockHash? offset) => + GetAccountState(ReservedAddresses.LegacyAccount, offset).GetBalance(address, currency); public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - GetAccountState(offset).GetTotalSupply(currency); + GetAccountState(ReservedAddresses.LegacyAccount, offset).GetTotalSupply(currency); public ValidatorSet GetValidatorSet(BlockHash? offset) => - GetAccountState(offset).GetValidatorSet(); + GetAccountState(ReservedAddresses.LegacyAccount, offset).GetValidatorSet(); - public IAccountState GetAccountState(BlockHash? offset) => - new MockAccount(offset, null); + public IWorldState GetWorldState(BlockHash? offset) + { + throw new System.NotImplementedException(); + } + + public IAccountState GetAccountState(Address address, BlockHash? offset) => + new MockAccount(address, null); public IAccountState GetAccountState(HashDigest? hash) => new MockAccount(null, hash); @@ -376,6 +383,11 @@ public ITrie GetTrie(BlockHash? offset) { throw new System.NotImplementedException(); } + + public ITrie GetTrie(HashDigest? hash) + { + throw new System.NotImplementedException(); + } } private class MockAccount : IAccount diff --git a/Libplanet/Blockchain/BlockChain.Evaluate.cs b/Libplanet/Blockchain/BlockChain.Evaluate.cs index 12c857ef779..7feadc02e6c 100644 --- a/Libplanet/Blockchain/BlockChain.Evaluate.cs +++ b/Libplanet/Blockchain/BlockChain.Evaluate.cs @@ -1,3 +1,4 @@ +#nullable disable using System; using System.Collections.Generic; using System.Diagnostics; @@ -114,7 +115,7 @@ public HashDigest DetermineBlockStateRootHash( return evaluations.Count > 0 ? evaluations.Last().OutputState - : GetAccountState(block.PreviousHash).Trie.Hash; + : GetWorldState(block.PreviousHash).Trie.Hash; } finally { diff --git a/Libplanet/Blockchain/BlockChain.TxExecution.cs b/Libplanet/Blockchain/BlockChain.TxExecution.cs index 7f381cbc0a9..df733074a54 100644 --- a/Libplanet/Blockchain/BlockChain.TxExecution.cs +++ b/Libplanet/Blockchain/BlockChain.TxExecution.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Libplanet.Action; -using Libplanet.Action.State; using Libplanet.Store.Trie; using Libplanet.Types.Blocks; using Libplanet.Types.Tx; @@ -48,7 +47,7 @@ IReadOnlyList evaluations } } - ITrie trie = GetAccountState(block.PreviousHash).Trie; + ITrie trie = GetWorldState(block.PreviousHash).Trie; int count = 0; foreach (var group in groupedEvals) diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index e9a57a7d2cc..9752f147ac3 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -481,31 +481,35 @@ public Transaction GetTransaction(TxId txId) /// . ///
/// 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) => - GetState(address, Tip.Hash); + public IValue GetState(Address address, Address accountAddress) => + GetState(address, accountAddress, Tip.Hash); /// - public IValue GetState(Address address, BlockHash? offset) => - GetAccountState(offset).GetState(address); + 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) => - GetStates(addresses, Tip.Hash); + public IReadOnlyList GetStates( + IReadOnlyList
addresses, Address accountAddress) => + GetStates(addresses, accountAddress, Tip.Hash); /// public IReadOnlyList GetStates( IReadOnlyList
addresses, + Address accountAddress, BlockHash? offset) => - GetAccountState(offset).GetStates(addresses); + GetAccountState(accountAddress, offset).GetStates(addresses); /// /// Queries 's current balance of the @@ -525,7 +529,7 @@ public FungibleAssetValue GetBalance( Address address, Currency currency, BlockHash? offset) => - GetAccountState(offset).GetBalance(address, currency); + GetAccountState(ReservedAddresses.LegacyAccount, offset).GetBalance(address, currency); /// /// Gets the current total supply of a in the @@ -539,21 +543,51 @@ public FungibleAssetValue GetTotalSupply(Currency currency) => /// public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - GetAccountState(offset).GetTotalSupply(currency); + GetAccountState(ReservedAddresses.LegacyAccount, offset).GetTotalSupply(currency); public ValidatorSet GetValidatorSet() => GetValidatorSet(Tip.Hash); /// public ValidatorSet GetValidatorSet(BlockHash? offset) => - GetAccountState(offset).GetValidatorSet(); + GetAccountState(ReservedAddresses.LegacyAccount, offset).GetValidatorSet(); + + /// + public IAccountState GetAccountState(Address address, BlockHash? offset) => + GetWorldState(offset).GetAccount(address); - /// - public IAccountState GetAccountState(BlockHash? offset) => - new AccountState(GetTrie(offset)); + public IWorldState GetWorldState() => GetWorldState(Tip.Hash); - /// - public IAccountState GetAccountState(HashDigest? hash) => - new AccountState(GetTrie(hash)); + /// + public IWorldState GetWorldState(BlockHash? offset) => + new WorldBaseState(GetTrie(offset), StateStore); + + public ITrie GetTrie(BlockHash? offset) + { + if (!(offset is { } hash)) + { + return StateStore.GetStateRoot(null); + } + else if (Store.GetStateRootHash(hash) is { } stateRootHash) + { + return GetTrie(stateRootHash); + } + else + { + throw new ArgumentException( + $"Could not find block hash {hash} in {nameof(IStore)}.", + nameof(offset)); + } + } + + 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)); + } /// /// Queries the recorded for a successful or failed @@ -1274,57 +1308,5 @@ internal void CleanupBlockCommitStore(long limit) } } } - - /// - /// Returns the state root associated with - /// . - /// - /// The to look up in - /// the internally held . - /// An representing the state root associated with - /// . - /// Thrown for one of the following reasons. - /// - /// - /// If is not and - /// cannot be found in . - /// - /// - /// If is not and - /// the state root hash associated with - /// cannot be found in . - /// - /// - /// - /// - /// An returned by this method is read-only. - /// - private ITrie GetTrie(BlockHash? offset) - { - if (!(offset is { } hash)) - { - return StateStore.GetStateRoot(null); - } - else if (Store.GetStateRootHash(hash) is { } stateRootHash) - { - return GetTrie(stateRootHash); - } - else - { - throw new ArgumentException( - $"Could not find block hash {hash} in {nameof(IStore)}.", - nameof(offset)); - } - } - - private 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)); - } } } From b106db080c1fa23ed0392fd0c3ca1d23a7738df4 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 22 Sep 2023 17:08:30 +0900 Subject: [PATCH 06/21] feat: Update Explorer Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit 429f999e5854547241c6d44f56927574be638701) --- Libplanet.Action/State/IBlockChainStates.cs | 86 +++++++--------- .../GraphTypes/AccountStateType.cs | 19 ++++ .../GraphTypes/WorldStateType.cs | 24 +++++ Libplanet.Explorer/Queries/StateQuery.cs | 97 +++++++++++++++---- Libplanet.Extensions.Cocona/Utils.cs | 2 +- Libplanet/Blockchain/BlockChain.cs | 55 ++++++++++- 6 files changed, 207 insertions(+), 76 deletions(-) create mode 100644 Libplanet.Explorer/GraphTypes/AccountStateType.cs create mode 100644 Libplanet.Explorer/GraphTypes/WorldStateType.cs diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index aa680991e8d..3676c912997 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -60,7 +60,7 @@ public interface IBlockChainStates /// 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 guarenteeed to be of the same + /// Hence, the returned is guaranteed to be of the same /// length as with possible values. /// /// Thrown when is not @@ -80,6 +80,23 @@ public interface IBlockChainStates Address accountAddress, BlockHash? offset); + + /// + /// Gets multiple states 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. + /// + IReadOnlyList GetStates( + IReadOnlyList
addresses, + HashDigest? stateRootHash); + /// /// Gets 's balance for given in the /// at . @@ -158,6 +175,21 @@ FungibleAssetValue GetTotalSupply( /// 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 . @@ -183,57 +215,5 @@ FungibleAssetValue GetTotalSupply( /// /// IAccountState GetAccountState(Address address, BlockHash? offset); - - /// - /// Returns the state root associated with - /// . - /// - /// The to look up in - /// the internally held . - /// An representing the state root associated with - /// . - /// Thrown for one of the following reasons. - /// - /// - /// If is not and - /// cannot be found in . - /// - /// - /// If is not and - /// the state root hash associated with - /// cannot be found in . - /// - /// - /// - /// - /// An returned by this method is read-only. - /// - ITrie GetTrie(BlockHash? offset); - - /// - /// Returns the state trie associated with - /// . - /// - /// The to look up in - /// the internally held . - /// An representing the state root associated with - /// . - /// Thrown for one of the following reasons. - /// - /// - /// If is not and - /// cannot be found in . - /// - /// - /// If is not and - /// the state root hash associated with - /// cannot be found in . - /// - /// - /// - /// - /// An returned by this method is read-only. - /// - ITrie GetTrie(HashDigest? hash); } } diff --git a/Libplanet.Explorer/GraphTypes/AccountStateType.cs b/Libplanet.Explorer/GraphTypes/AccountStateType.cs new file mode 100644 index 00000000000..9e486c9ddf3 --- /dev/null +++ b/Libplanet.Explorer/GraphTypes/AccountStateType.cs @@ -0,0 +1,19 @@ +using GraphQL.Types; +using Libplanet.Action.State; + +namespace Libplanet.Explorer.GraphTypes +{ + public class AccountStateType : ObjectGraphType + { + public AccountStateType() + { + Name = "AccountState"; + + Field( + name: "StateRootHash", + description: "The state root hash of the account state.", + resolve: ctx => ctx.Source.Trie.Hash.ToString() + ); + } + } +} diff --git a/Libplanet.Explorer/GraphTypes/WorldStateType.cs b/Libplanet.Explorer/GraphTypes/WorldStateType.cs new file mode 100644 index 00000000000..99a785fdea5 --- /dev/null +++ b/Libplanet.Explorer/GraphTypes/WorldStateType.cs @@ -0,0 +1,24 @@ +using GraphQL.Types; +using Libplanet.Action.State; + +namespace Libplanet.Explorer.GraphTypes +{ + public class WorldStateType : ObjectGraphType + { + public WorldStateType() + { + Name = "WorldState"; + + Field>( + name: "StateRootHash", + description: "The state root hash of the world state.", + resolve: ctx => ctx.Source.Trie.Hash.ToString() + ); + Field>( + name: "Legacy", + description: "The legacy flag of the world state.", + resolve: ctx => ctx.Source.Legacy + ); + } + } +} diff --git a/Libplanet.Explorer/Queries/StateQuery.cs b/Libplanet.Explorer/Queries/StateQuery.cs index 4b2e9ce4e52..2b8a1c48ab8 100644 --- a/Libplanet.Explorer/Queries/StateQuery.cs +++ b/Libplanet.Explorer/Queries/StateQuery.cs @@ -18,11 +18,27 @@ public class StateQuery public StateQuery() { Name = "StateQuery"; + Field>( + "worldState", + arguments: new QueryArguments( + new QueryArgument> { Name = "offsetBlockHash" } + ), + resolve: ResolveWorldState + ); + Field>( + "accountState", + arguments: new QueryArguments( + new QueryArgument> { Name = "accountAddress" }, + new QueryArgument> { Name = "offsetBlockHash" } + ), + resolve: ResolveAccountState + ); Field>>( "states", arguments: new QueryArguments( new QueryArgument>>> - { Name = "addresses" }, + { Name = "addresses" }, + new QueryArgument> { Name = "accountAddress" }, new QueryArgument { Name = "offsetBlockHash" }, new QueryArgument { Name = "offsetStateRootHash" } ), @@ -57,10 +73,54 @@ public StateQuery() ); } + private static object ResolveWorldState( + IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) + { + string offsetBlockHash = context.GetArgument("offsetBlockHash"); + + 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); + } + + private static object ResolveAccountState( + IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) + { + Address accountAddress = context.GetArgument
("accountAddress"); + string offsetBlockHash = context.GetArgument("offsetBlockHash"); + + 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.GetAccountState(accountAddress, offset); + } + private static object ResolveStates( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { Address[] addresses = context.GetArgument("addresses"); + Address accountAddress = context.GetArgument
("accountAddress"); string? offsetBlockHash = context.GetArgument("offsetBlockHash"); HashDigest? offsetStateRootHash = context .GetArgument?>("offsetStateRootHash"); @@ -90,15 +150,15 @@ private static object ResolveStates( ); } - return context.Source.ChainStates.GetAccountState( + return context.Source.ChainStates.GetStates( + addresses, + accountAddress, offset - ).GetStates(addresses); + ); } case (_, srh: not null): - return context.Source.ChainStates.GetAccountState( - offsetStateRootHash - ).GetStates(addresses); + return context.Source.ChainStates.GetStates(addresses, offsetStateRootHash); } } @@ -136,9 +196,7 @@ private static object ResolveBalance( ); } - return context.Source.ChainStates.GetAccountState( - offset - ).GetBalance(owner, currency); + return context.Source.ChainStates.GetBalance(owner, currency, offset); } case (_, srh: not null): @@ -190,15 +248,13 @@ private static object ResolveBalance( ); } - return context.Source.ChainStates.GetAccountState( - offset - ).GetTotalSupply(currency); + return context.Source.ChainStates.GetTotalSupply(currency, offset); } case (_, srh: not null): - return context.Source.ChainStates.GetAccountState( - offsetStateRootHash - ).GetTotalSupply(currency); + return context.Source.ChainStates.GetWorldState(offsetStateRootHash) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetTotalSupply(currency); } } @@ -234,15 +290,14 @@ private static object ResolveBalance( ); } - return context.Source.ChainStates.GetAccountState( - offset - ).GetValidatorSet().Validators; + return context.Source.ChainStates.GetValidatorSet(offset).Validators; } case (_, srh: not null): - return context.Source.ChainStates.GetAccountState( - offsetStateRootHash - ).GetValidatorSet().Validators; + return context.Source.ChainStates.GetWorldState(offsetStateRootHash) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetValidatorSet() + .Validators; } } } diff --git a/Libplanet.Extensions.Cocona/Utils.cs b/Libplanet.Extensions.Cocona/Utils.cs index 0aeef911fe1..29d6270d93d 100644 --- a/Libplanet.Extensions.Cocona/Utils.cs +++ b/Libplanet.Extensions.Cocona/Utils.cs @@ -260,7 +260,7 @@ public DummyAction() public IValue PlainValue { get; private set; } /// - public IAccount Execute(IActionContext context) => + public IWorld Execute(IActionContext context) => context.PreviousState; /// diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 9752f147ac3..a96f12a461e 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -23,6 +23,7 @@ using Libplanet.Types.Consensus; using Libplanet.Types.Tx; using Serilog; +using static Libplanet.Action.State.KeyConverters; namespace Libplanet.Blockchain { @@ -504,13 +505,23 @@ 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 . @@ -561,6 +572,34 @@ public IAccountState GetAccountState(Address address, BlockHash? offset) => public IWorldState GetWorldState(BlockHash? offset) => new WorldBaseState(GetTrie(offset), StateStore); + /// + public IWorldState GetWorldState(HashDigest? hash) => + new WorldBaseState(GetTrie(hash), StateStore); + + /// + /// Returns the state root associated with + /// . + /// + /// The to look up in + /// the internally held . + /// An representing the state root associated with + /// . + /// Thrown for one of the following reasons. + /// + /// + /// If is not and + /// cannot be found in . + /// + /// + /// If is not and + /// the state root hash associated with + /// cannot be found in . + /// + /// + /// + /// + /// An returned by this method is read-only. + /// public ITrie GetTrie(BlockHash? offset) { if (!(offset is { } hash)) @@ -579,6 +618,20 @@ public ITrie GetTrie(BlockHash? offset) } } + /// + /// Returns the state trie associated with + /// . + /// + /// 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); From 1acb161d50bb653b3d9ffe25f530bb03a08b8e45 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 25 Sep 2023 09:58:12 +0900 Subject: [PATCH 07/21] fix: Update command Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit c71f8b6a0f6b13c94add18c486879924af212e83) --- Libplanet.Extensions.Cocona/Commands/BlockCommand.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs b/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs index 882c869fb6e..e5975a47632 100644 --- a/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs +++ b/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs @@ -138,6 +138,8 @@ public void GenerateGenesis( .Select(PublicKey.FromHex) .Select(k => new Validator(k, BigInteger.One)) .ToList()); + var emptyState = + ImmutableTrieDictionary>.Empty; ImmutableList txs = Array.Empty() // FIXME: Remove this pragma after fixing the following issue: @@ -150,7 +152,7 @@ public void GenerateGenesis( null, new IAction[] { - new Initialize(validatorSet, ImmutableTrieDictionary.Empty), + new Initialize(validatorSet, emptyState), }.Select(x => x.PlainValue))) .ToImmutableList(); From ec763db15fa55f2d6a740922e8b6650cf9fc53b7 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Mon, 25 Sep 2023 09:58:33 +0900 Subject: [PATCH 08/21] test: Update tests Co-authored-by: Chanhyuck Ko Co-authored-by: Lee, Suho (cherry picked from commit 9ab368f1b8a524c29bf8e6d03020c4a2efde28ea) --- Libplanet.Action.Tests/ActionContextTest.cs | 10 +- .../ActionEvaluationExtensions.cs | 12 +- .../ActionEvaluationTest.cs | 13 +- Libplanet.Action.Tests/Common/Attack.cs | 10 +- Libplanet.Action.Tests/Common/BaseAction.cs | 2 +- Libplanet.Action.Tests/Common/DelayAction.cs | 7 +- .../Common/DetectRehearsal.cs | 15 +- Libplanet.Action.Tests/Common/DumbAction.cs | 23 +- .../Common/DumbModernAction.cs | 321 ++++++++++++++++++ Libplanet.Action.Tests/Common/MinerReward.cs | 14 +- Libplanet.Action.Tests/Common/RandomAction.cs | 12 +- .../Common/SetStatesAtBlock.cs | 14 +- Libplanet.Action.Tests/Common/SetValidator.cs | 7 +- Libplanet.Action.Tests/Common/Sleep.cs | 2 +- .../Common/ThrowException.cs | 2 +- .../Loader/TypedActionLoaderTest.cs | 6 +- .../Mocks/MockAccountState.cs | 21 +- .../Mocks/MockWorldState.cs | 42 +++ Libplanet.Action.Tests/Sys/InitializeTest.cs | 33 +- Libplanet.Action.Tests/Sys/RegistryTest.cs | 21 +- Libplanet.Action.Tests/TestUtils.cs | 60 +++- Libplanet.Action/State/IBlockChainStates.cs | 12 + .../ActionAnalyzerTest.cs | 4 +- .../GeneratedBlockChainFixture.cs | 3 +- .../Queries/StateQueryTest.cs | 215 ++++++++---- .../Queries/TransactionQueryTest.cs | 10 +- Libplanet.Explorer.Tests/SimpleAction.cs | 4 +- Libplanet.Explorer/Queries/StateQuery.cs | 6 +- Libplanet.Net.Tests/SwarmTest.Preload.cs | 10 +- Libplanet.Net.Tests/SwarmTest.cs | 29 +- Libplanet.Tests/Action/AccountDiffTest.cs | 11 +- Libplanet.Tests/Action/AccountV0Test.cs | 14 +- Libplanet.Tests/Action/AccountV1Test.cs | 15 +- Libplanet.Tests/Action/ActionEvaluatorTest.cs | 191 +++++++---- .../Blockchain/BlockChainTest.Append.cs | 98 ++++-- .../Blockchain/BlockChainTest.ProposeBlock.cs | 62 ++-- .../BlockChainTest.ValidateNextBlock.cs | 2 +- Libplanet.Tests/Blockchain/BlockChainTest.cs | 105 ++++-- .../Renderers/AnonymousActionRendererTest.cs | 12 +- .../Renderers/LoggedActionRendererTest.cs | 8 +- .../Blocks/PreEvaluationBlockTest.cs | 19 +- Libplanet.Tests/Fixtures/Arithmetic.cs | 11 +- .../Fixtures/BlockContentFixture.cs | 3 +- Libplanet.Tests/Fixtures/IntegerSet.cs | 14 +- Libplanet.Tests/Store/StoreTest.cs | 2 +- Libplanet.Tests/TestUtils.cs | 3 +- Libplanet.Tests/Tx/TransactionTest.cs | 27 +- Libplanet/Blockchain/BlockChain.cs | 6 +- 48 files changed, 1182 insertions(+), 361 deletions(-) create mode 100644 Libplanet.Action.Tests/Common/DumbModernAction.cs create mode 100644 Libplanet.Action.Tests/Mocks/MockWorldState.cs diff --git a/Libplanet.Action.Tests/ActionContextTest.cs b/Libplanet.Action.Tests/ActionContextTest.cs index 4044de57780..58344f4fe63 100644 --- a/Libplanet.Action.Tests/ActionContextTest.cs +++ b/Libplanet.Action.Tests/ActionContextTest.cs @@ -36,7 +36,7 @@ public void RandomShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: new Account(MockAccountState.Empty), + previousState: World.Create(new MockWorldState()), randomSeed: seed, gasLimit: 0 ); @@ -54,7 +54,7 @@ public void GuidShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: new Account(MockAccountState.Empty), + previousState: World.Create(new MockWorldState()), randomSeed: 0, gasLimit: 0 ); @@ -65,7 +65,7 @@ public void GuidShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: new Account(MockAccountState.Empty), + previousState: World.Create(new MockWorldState()), randomSeed: 0, gasLimit: 0 ); @@ -76,7 +76,7 @@ public void GuidShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: new Account(MockAccountState.Empty), + previousState: World.Create(new MockWorldState()), randomSeed: 1, gasLimit: 0 ); @@ -115,7 +115,7 @@ public void GuidVersionAndVariant() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: new Account(MockAccountState.Empty), + previousState: World.Create(new MockWorldState()), randomSeed: i, gasLimit: 0 ); diff --git a/Libplanet.Action.Tests/ActionEvaluationExtensions.cs b/Libplanet.Action.Tests/ActionEvaluationExtensions.cs index e158dafbaea..7fd1b273ab4 100644 --- a/Libplanet.Action.Tests/ActionEvaluationExtensions.cs +++ b/Libplanet.Action.Tests/ActionEvaluationExtensions.cs @@ -13,7 +13,9 @@ this IEnumerable evaluations ) => evaluations.Aggregate( ImmutableDictionary.Empty, - (dirty, ev) => dirty.SetItems(ev.OutputState.GetUpdatedStates()) + (dirty, ev) => dirty.SetItems( + ev.OutputState.GetAccount( + ReservedAddresses.LegacyAccount).GetUpdatedStates()) ); public static IImmutableDictionary<(Address, Currency), FungibleAssetValue> @@ -22,14 +24,18 @@ this IEnumerable evaluations ) => evaluations.Aggregate( ImmutableDictionary<(Address, Currency), FungibleAssetValue>.Empty, - (dirty, ev) => dirty.SetItems(ev.OutputState.GetUpdatedBalances()) + (dirty, ev) => dirty.SetItems( + ev.OutputState.GetAccount( + ReservedAddresses.LegacyAccount).GetUpdatedBalances()) ); public static IImmutableDictionary GetDirtyTotalSupplies(this IEnumerable evaluations) => evaluations.Aggregate( ImmutableDictionary.Empty, - (dirty, ev) => dirty.SetItems(ev.OutputState.GetUpdatedTotalSupplies()) + (dirty, ev) => dirty.SetItems( + ev.OutputState.GetAccount( + ReservedAddresses.LegacyAccount).GetUpdatedTotalSupplies()) ); } } diff --git a/Libplanet.Action.Tests/ActionEvaluationTest.cs b/Libplanet.Action.Tests/ActionEvaluationTest.cs index e71f5af1457..663b2740a5f 100644 --- a/Libplanet.Action.Tests/ActionEvaluationTest.cs +++ b/Libplanet.Action.Tests/ActionEvaluationTest.cs @@ -37,11 +37,15 @@ public void Constructor() address, 1, Block.CurrentProtocolVersion, - new Account(MockAccountState.Empty), + World.Create(new MockWorldState()), 123, 0, false), - new Account(MockAccountState.Empty.SetState(address, (Text)"item"))); + World.Create( + new MockWorldState().SetAccount( + ReservedAddresses.LegacyAccount, + new Account(MockAccountState.Empty.SetState(address, (Text)"item")))) + ); var action = (DumbAction)evaluation.Action; Assert.Equal(address, action.TargetAddress); @@ -51,11 +55,12 @@ public void Constructor() Assert.Equal(address, evaluation.InputContext.Miner); Assert.Equal(1, evaluation.InputContext.BlockIndex); Assert.Null( - evaluation.InputContext.PreviousState.GetState(address) + evaluation.InputContext.PreviousState.GetAccount( + ReservedAddresses.LegacyAccount).GetState(address) ); Assert.Equal( (Text)"item", - evaluation.OutputState.GetState(address) + evaluation.OutputState.GetAccount(ReservedAddresses.LegacyAccount).GetState(address) ); } } diff --git a/Libplanet.Action.Tests/Common/Attack.cs b/Libplanet.Action.Tests/Common/Attack.cs index 679a319e46f..62b3ad40bcc 100644 --- a/Libplanet.Action.Tests/Common/Attack.cs +++ b/Libplanet.Action.Tests/Common/Attack.cs @@ -29,13 +29,14 @@ public override void LoadPlainValue(IValue plainValue) TargetAddress = new Address(values["target_address"]); } - public override IAccount Execute(IActionContext context) + public override IWorld Execute(IActionContext context) { IImmutableSet usedWeapons = ImmutableHashSet.Empty; IImmutableSet targets = ImmutableHashSet.Empty; - IAccount previousState = context.PreviousState; + IWorld previousState = context.PreviousState; + IAccount legacyAccount = previousState.GetAccount(ReservedAddresses.LegacyAccount); - object value = previousState.GetState(TargetAddress); + object value = legacyAccount.GetState(TargetAddress); if (!ReferenceEquals(value, null)) { var previousResult = BattleResult.FromBencodex((Bencodex.Types.Dictionary)value); @@ -46,8 +47,9 @@ public override IAccount Execute(IActionContext context) usedWeapons = usedWeapons.Add(Weapon); targets = targets.Add(Target); var result = new BattleResult(usedWeapons, targets); + legacyAccount = legacyAccount.SetState(TargetAddress, result.ToBencodex()); - return previousState.SetState(TargetAddress, result.ToBencodex()); + return previousState.SetAccount(ReservedAddresses.LegacyAccount, legacyAccount); } } } diff --git a/Libplanet.Action.Tests/Common/BaseAction.cs b/Libplanet.Action.Tests/Common/BaseAction.cs index c36fb8f99e9..d7578d12a8c 100644 --- a/Libplanet.Action.Tests/Common/BaseAction.cs +++ b/Libplanet.Action.Tests/Common/BaseAction.cs @@ -15,7 +15,7 @@ public abstract class BaseAction : IAction public abstract IValue PlainValue { get; } - public abstract IAccount Execute(IActionContext context); + public abstract IWorld Execute(IActionContext context); public abstract void LoadPlainValue(IValue plainValue); diff --git a/Libplanet.Action.Tests/Common/DelayAction.cs b/Libplanet.Action.Tests/Common/DelayAction.cs index a39bb5c75dc..bac1f8f75a1 100644 --- a/Libplanet.Action.Tests/Common/DelayAction.cs +++ b/Libplanet.Action.Tests/Common/DelayAction.cs @@ -31,7 +31,7 @@ public IValue PlainValue } } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { var state = context.PreviousState; var started = DateTimeOffset.UtcNow; @@ -41,7 +41,10 @@ public IAccount Execute(IActionContext context) MilliSecond); Thread.Sleep(MilliSecond); var ended = DateTimeOffset.UtcNow; - state = state.SetState(TrivialUpdatedAddress, new Bencodex.Types.Integer(MilliSecond)); + var delayAccount = state + .GetAccount(ReservedAddresses.LegacyAccount) + .SetState(TrivialUpdatedAddress, new Bencodex.Types.Integer(MilliSecond)); + state = state.SetAccount(ReservedAddresses.LegacyAccount, delayAccount); Log.Debug( "{MethodName} Total Executed Time: {Elapsed}. Delay target: {MilliSecond}", nameof(DelayAction), diff --git a/Libplanet.Action.Tests/Common/DetectRehearsal.cs b/Libplanet.Action.Tests/Common/DetectRehearsal.cs index cd0b2c86aea..a0c7fc948ef 100644 --- a/Libplanet.Action.Tests/Common/DetectRehearsal.cs +++ b/Libplanet.Action.Tests/Common/DetectRehearsal.cs @@ -22,14 +22,17 @@ public override void LoadPlainValue(IValue plainValue) TargetAddress = new Address(values["target_address"]); } - public override IAccount Execute(IActionContext context) + public override IWorld Execute(IActionContext context) { - IAccount previousState = context.PreviousState; + IWorld previousState = context.PreviousState; + IAccount legacyAccount = previousState.GetAccount(ReservedAddresses.LegacyAccount); ResultState = context.Rehearsal; - return previousState.SetState( - TargetAddress, - new Bencodex.Types.Boolean(context.Rehearsal) - ); + return previousState.SetAccount( + ReservedAddresses.LegacyAccount, + legacyAccount.SetState( + TargetAddress, + new Bencodex.Types.Boolean(context.Rehearsal) + )); } } } diff --git a/Libplanet.Action.Tests/Common/DumbAction.cs b/Libplanet.Action.Tests/Common/DumbAction.cs index 9ce03d08027..fe2d64ec9be 100644 --- a/Libplanet.Action.Tests/Common/DumbAction.cs +++ b/Libplanet.Action.Tests/Common/DumbAction.cs @@ -131,7 +131,7 @@ public IValue PlainValue } } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { if (RehearsalRecords.Value is null) { @@ -144,13 +144,14 @@ public IAccount Execute(IActionContext context) RehearsalRecords.Value.Add((TargetAddress, Item)); } - IAccount states = context.PreviousState; + IWorld world = context.PreviousState; if (Item is null) { - return states; + return world; } - string items = (Text?)states.GetState(TargetAddress); + IAccount account = world.GetAccount(ReservedAddresses.LegacyAccount); + string items = (Text?)account.GetState(TargetAddress); string item = RecordRehearsal ? $"{Item}:{context.Rehearsal}" : Item; @@ -176,7 +177,7 @@ public IAccount Execute(IActionContext context) if (RecordRandom) { - states = states.SetState( + account = account.SetState( RandomRecordsAddress, (Integer)context.GetRandom().Next() ); @@ -187,11 +188,11 @@ public IAccount Execute(IActionContext context) Item = Item.ToUpperInvariant(); } - IAccount nextState = states.SetState(TargetAddress, (Text)items); + account = account.SetState(TargetAddress, (Text)items); if (!(Transfer is null)) { - nextState = nextState.TransferAsset( + account = account.TransferAsset( context, sender: Transfer.Item1, recipient: Transfer.Item2, @@ -202,8 +203,8 @@ public IAccount Execute(IActionContext context) if (!(Validators is null)) { - nextState = Validators.Aggregate( - nextState, + account = Validators.Aggregate( + account, (current, validator) => current.SetValidator(new Validator(validator, BigInteger.One))); } @@ -216,11 +217,11 @@ public IAccount Execute(IActionContext context) ExecuteRecords.Value = ExecuteRecords.Value.Add(new ExecuteRecord() { Action = this, - NextState = nextState, + NextState = account, Rehearsal = context.Rehearsal, }); - return nextState; + return world.SetAccount(ReservedAddresses.LegacyAccount, account); } public void LoadPlainValue(IValue plainValue) diff --git a/Libplanet.Action.Tests/Common/DumbModernAction.cs b/Libplanet.Action.Tests/Common/DumbModernAction.cs new file mode 100644 index 00000000000..9e3b2852688 --- /dev/null +++ b/Libplanet.Action.Tests/Common/DumbModernAction.cs @@ -0,0 +1,321 @@ +using System.Collections.Immutable; +using System.Globalization; +using System.Numerics; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Libplanet.Types.Consensus; +using Boolean = Bencodex.Types.Boolean; + +namespace Libplanet.Action.Tests.Common +{ + public sealed class DumbModernAction : IAction, IEquatable + { + public static readonly Address DumbModernAddress = + new Address("0123456789abcdef0123456789abcdef12345678"); + + public static readonly Address RandomRecordsAddress = + new Address("7811C3fAa0f9Cc41F7971c3d9b031B1095b20AB2"); + + public static readonly Currency DumbCurrency = + Currency.Uncapped("DUMB", 0, null); + + public DumbModernAction() + { + } + + public DumbModernAction(IEnumerable validators) + { + Validators = validators; + } + + public DumbModernAction( + Address targetAddress, + string item, + bool recordRehearsal = false, + bool recordRandom = false, + bool idempotent = false, + Tuple transfer = null) + { + Idempotent = idempotent; + TargetAddress = targetAddress; + Item = item; + RecordRehearsal = recordRehearsal; + RecordRandom = recordRandom; + Transfer = transfer; + } + + public DumbModernAction( + Address targetAddress, + string item, + Address transferFrom, + Address transferTo, + BigInteger transferAmount, + bool recordRehearsal = false, + bool recordRandom = false, + bool idempotent = false + ) + : this( + targetAddress, + item, + recordRehearsal, + recordRandom, + idempotent, + Tuple.Create(transferFrom, transferTo, transferAmount) + ) + { + } + + public static AsyncLocal> + ExecuteRecords + { get; } = new AsyncLocal>(); + + public static AsyncLocal> + RehearsalRecords + { get; } = + new AsyncLocal>(); + + public Address TargetAddress { get; private set; } + + public string Item { get; private set; } + + public bool RecordRehearsal { get; private set; } + + public bool RecordRandom { get; private set; } + + public bool Idempotent { get; private set; } + + public Tuple Transfer { get; private set; } + + public IEnumerable Validators { get; private set; } + + public IValue PlainValue + { + get + { + var plainValue = Bencodex.Types.Dictionary.Empty; + if (!(Item is null)) + { + plainValue = new Bencodex.Types.Dictionary( + new Dictionary + { + ["item"] = (Text)Item, + ["target_address"] = new Binary(TargetAddress.ByteArray), + ["record_rehearsal"] = new Bencodex.Types.Boolean(RecordRehearsal), + }); + } + + if (RecordRandom) + { + // In order to avoid changing tx signatures in many test + // fixtures, adds field only if RecordRandom = true. + plainValue = plainValue.Add("record_random", true); + } + + if (Idempotent) + { + plainValue = plainValue.Add("idempotent", Idempotent); + } + + if (!(Transfer is null)) + { + plainValue = plainValue + .Add("transfer_from", Transfer.Item1.ByteArray) + .Add("transfer_to", Transfer.Item2.ByteArray) + .Add("transfer_amount", Transfer.Item3); + } + + if (!(Validators is null)) + { + plainValue = plainValue + .Add("validators", new List(Validators.Select(p => p.Format(false)))); + } + + return plainValue; + } + } + + public IWorld Execute(IActionContext context) + { + if (RehearsalRecords.Value is null) + { + RehearsalRecords.Value = ImmutableList<(Address, string)>.Empty; + } + + if (context.Rehearsal) + { + RehearsalRecords.Value = + RehearsalRecords.Value.Add((TargetAddress, Item)); + } + + IWorld world = context.PreviousState; + if (Item is null) + { + return world; + } + + IAccount account = world.GetAccount(DumbModernAddress); + string items = (Text?)account.GetState(TargetAddress); + string item = RecordRehearsal + ? $"{Item}:{context.Rehearsal}" + : Item; + + if (Idempotent) + { + var splitItems = items is null ? new[] { item } : (items + "," + item).Split(','); + items = string.Join( + ",", + splitItems.OrderBy(x => + float.Parse( + x.Substring(4), + NumberStyles.Float, + CultureInfo.InvariantCulture + ) + ) + ); + } + else + { + items = items is null ? item : $"{items},{item}"; + } + + if (RecordRandom) + { + account = account.SetState( + RandomRecordsAddress, + (Integer)context.GetRandom().Next() + ); + } + + if (Item.Equals("D") && !context.Rehearsal) + { + Item = Item.ToUpperInvariant(); + } + + account = account.SetState(TargetAddress, (Text)items); + + if (!(Transfer is null)) + { + account = account.TransferAsset( + context, + sender: Transfer.Item1, + recipient: Transfer.Item2, + value: FungibleAssetValue.FromRawValue(DumbCurrency, Transfer.Item3), + allowNegativeBalance: true + ); + } + + if (!(Validators is null)) + { + account = Validators.Aggregate( + account, + (current, validator) => + current.SetValidator(new Validator(validator, BigInteger.One))); + } + + if (ExecuteRecords.Value is null) + { + ExecuteRecords.Value = ImmutableList.Empty; + } + + ExecuteRecords.Value = ExecuteRecords.Value.Add(new ExecuteRecord() + { + Action = this, + NextState = account, + Rehearsal = context.Rehearsal, + }); + + world = world.SetAccount(DumbModernAddress, account); + return world; + } + + public void LoadPlainValue(IValue plainValue) + { + LoadPlainValue((Bencodex.Types.Dictionary)plainValue); + } + + public void LoadPlainValue(Dictionary plainValue) + { + Item = plainValue.GetValue("item"); + TargetAddress = new Address(plainValue.GetValue("target_address")); + RecordRehearsal = plainValue.GetValue("record_rehearsal").Value; + RecordRandom = + plainValue.ContainsKey((IKey)(Text)"record_random") && + plainValue["record_random"] is Boolean r && + r.Value; + + if (plainValue.ContainsKey((IKey)(Text)"idempotent")) + { + Idempotent = plainValue.GetValue("idempotent"); + } + + if (plainValue.TryGetValue((Text)"transfer_from", out IValue from) && + plainValue.TryGetValue((Text)"transfer_to", out IValue to) && + plainValue.TryGetValue((Text)"transfer_amount", out IValue a) && + a is Integer amount) + { + Transfer = Tuple.Create(new Address(from), new Address(to), amount.Value); + } + + if (plainValue.ContainsKey((IKey)(Text)"validators")) + { + Validators = plainValue.GetValue("validators") + .Select(value => new PublicKey(((Binary)value).ByteArray)); + } + } + + public bool Equals(DumbModernAction other) + { + return !(other is null) && ( + ReferenceEquals(this, other) || ( + TargetAddress.Equals(other.TargetAddress) && + string.Equals(Item, other.Item) && + RecordRehearsal == other.RecordRehearsal + ) + ); + } + + public override bool Equals(object obj) + { + return !(obj is null) && ( + ReferenceEquals(this, obj) || + (obj is DumbModernAction other && Equals(other)) + ); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = TargetAddress.GetHashCode(); + hashCode = (hashCode * 397) ^ + (Item != null ? Item.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ RecordRehearsal.GetHashCode(); + return hashCode; + } + } + + public override string ToString() + { + const string T = "true", F = "false"; + string transfer = Transfer is Tuple t + ? $"({t.Item1}, {t.Item2}, {t.Item3})" + : "null"; + string validators = Validators is null + ? "none" + : Validators + .Aggregate(string.Empty, (s, key) => s + key.Format(false) + ", ") + .TrimEnd(',', ' '); + return $"{nameof(DumbModernAction)} {{ " + + $"{nameof(TargetAddress)} = {TargetAddress}, " + + $"{nameof(Item)} = {Item ?? string.Empty}, " + + $"{nameof(RecordRehearsal)} = {(RecordRehearsal ? T : F)}, " + + $"{nameof(RecordRandom)} = {(RecordRandom ? T : F)}, " + + $"{nameof(Idempotent)} = {(Idempotent ? T : F)}, " + + $"{nameof(Transfer)} = {transfer} " + + $"{nameof(Validators)} = {validators} " + + "}"; + } + } +} diff --git a/Libplanet.Action.Tests/Common/MinerReward.cs b/Libplanet.Action.Tests/Common/MinerReward.cs index e1a4a49b376..957c30bdec6 100644 --- a/Libplanet.Action.Tests/Common/MinerReward.cs +++ b/Libplanet.Action.Tests/Common/MinerReward.cs @@ -36,23 +36,25 @@ public void LoadPlainValue(Dictionary plainValue) Reward = (Integer)plainValue["reward"]; } - public IAccount Execute(IActionContext ctx) + public IWorld Execute(IActionContext ctx) { - IAccount states = ctx.PreviousState; + IWorld states = ctx.PreviousState; + IAccount legacyAccount = states.GetAccount(ReservedAddresses.LegacyAccount); - string rewardRecord = (Text?)states.GetState(RewardRecordAddress); + string rewardRecord = (Text?)legacyAccount.GetState(RewardRecordAddress); rewardRecord = rewardRecord is null ? ctx.Miner.ToString() : $"{rewardRecord},{ctx.Miner}"; - states = states.SetState(RewardRecordAddress, (Text)rewardRecord); + legacyAccount = legacyAccount.SetState(RewardRecordAddress, (Text)rewardRecord); - IValue tempQualifier1 = states?.GetState(ctx.Miner); + IValue tempQualifier1 = legacyAccount.GetState(ctx.Miner); int previousReward = tempQualifier1 is Integer i ? (int)i.Value : 0; int reward = previousReward + Reward; - return states.SetState(ctx.Miner, (Integer)reward); + legacyAccount = legacyAccount.SetState(ctx.Miner, (Integer)reward); + return states.SetAccount(ReservedAddresses.LegacyAccount, legacyAccount); } } } diff --git a/Libplanet.Action.Tests/Common/RandomAction.cs b/Libplanet.Action.Tests/Common/RandomAction.cs index 3811b2b6120..f91d86b8406 100644 --- a/Libplanet.Action.Tests/Common/RandomAction.cs +++ b/Libplanet.Action.Tests/Common/RandomAction.cs @@ -28,15 +28,19 @@ public void LoadPlainValue(IValue plainValue) Address = new Address((string)(Text)dictionary["address"]); } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { - IAccount states = context.PreviousState; + IWorld states = context.PreviousState; + IAccount legacyAccount = states.GetAccount(ReservedAddresses.LegacyAccount); if (context.Rehearsal) { - return states.SetState(Address, Null.Value); + return states.SetAccount( + ReservedAddresses.LegacyAccount, + legacyAccount.SetState(Address, Null.Value)); } - return states.SetState(Address, (Integer)context.GetRandom().Next()); + legacyAccount = legacyAccount.SetState(Address, (Integer)context.GetRandom().Next()); + return states.SetAccount(ReservedAddresses.LegacyAccount, legacyAccount); } } } diff --git a/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs b/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs index 5923bbf3a89..360b43d22db 100644 --- a/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs +++ b/Libplanet.Action.Tests/Common/SetStatesAtBlock.cs @@ -8,22 +8,26 @@ public class SetStatesAtBlock : IAction { private Address _address; private IValue _value = Null.Value; + private Address _accountAddress; private long _blockIndex; public SetStatesAtBlock() { } - public SetStatesAtBlock(Address address, IValue value, long blockIndex) + public SetStatesAtBlock( + Address address, IValue value, Address accountAddress, long blockIndex) { _address = address; _blockIndex = blockIndex; + _accountAddress = accountAddress; _value = value; } public IValue PlainValue => Bencodex.Types.Dictionary.Empty .Add("address", _address.ByteArray) .Add("value", _value) + .Add("account_address", _accountAddress.ByteArray) .Add("block_index", _blockIndex); public void LoadPlainValue(IValue plainValue) @@ -31,15 +35,17 @@ public void LoadPlainValue(IValue plainValue) var dict = (Bencodex.Types.Dictionary)plainValue; _address = new Address(dict["address"]); _value = dict["value"]; + _accountAddress = new Address(dict["account_address"]); _blockIndex = (Integer)dict["block_index"]; } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { - IAccount states = context.PreviousState; + IWorld states = context.PreviousState; + IAccount account = states.GetAccount(_accountAddress); if (context.BlockIndex == _blockIndex) { - states = states.SetState(_address, _value); + states = states.SetAccount(_accountAddress, account.SetState(_address, _value)); } return states; diff --git a/Libplanet.Action.Tests/Common/SetValidator.cs b/Libplanet.Action.Tests/Common/SetValidator.cs index cbd3872768e..20fde9391df 100644 --- a/Libplanet.Action.Tests/Common/SetValidator.cs +++ b/Libplanet.Action.Tests/Common/SetValidator.cs @@ -37,9 +37,12 @@ public void LoadPlainValue(IValue plainValue) } /// - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { - return context.PreviousState.SetValidator(Validator); + IWorld states = context.PreviousState; + IAccount legacyAccount = states.GetAccount(ReservedAddresses.LegacyAccount); + return states.SetAccount( + ReservedAddresses.LegacyAccount, legacyAccount.SetValidator(Validator)); } /// diff --git a/Libplanet.Action.Tests/Common/Sleep.cs b/Libplanet.Action.Tests/Common/Sleep.cs index cdee4b20f46..98f8b2fd836 100644 --- a/Libplanet.Action.Tests/Common/Sleep.cs +++ b/Libplanet.Action.Tests/Common/Sleep.cs @@ -13,7 +13,7 @@ public class Sleep : BaseAction .Add("values", Dictionary.Empty .Add("zone_id", ZoneId)); - public override IAccount Execute(IActionContext context) + public override IWorld Execute(IActionContext context) { // No-op. return context.PreviousState; diff --git a/Libplanet.Action.Tests/Common/ThrowException.cs b/Libplanet.Action.Tests/Common/ThrowException.cs index 1b41b918c3b..c5daa541651 100644 --- a/Libplanet.Action.Tests/Common/ThrowException.cs +++ b/Libplanet.Action.Tests/Common/ThrowException.cs @@ -36,7 +36,7 @@ public void LoadPlainValue(Dictionary plainValue) Deterministic = (Boolean)plainValue["deterministic"]; } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { if (context.Rehearsal ? ThrowOnRehearsal : ThrowOnExecution) { diff --git a/Libplanet.Action.Tests/Loader/TypedActionLoaderTest.cs b/Libplanet.Action.Tests/Loader/TypedActionLoaderTest.cs index 688d300d057..c386f58ee21 100644 --- a/Libplanet.Action.Tests/Loader/TypedActionLoaderTest.cs +++ b/Libplanet.Action.Tests/Loader/TypedActionLoaderTest.cs @@ -1,5 +1,6 @@ using Bencodex.Types; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Action.Tests.Common; using Libplanet.Crypto; @@ -47,7 +48,10 @@ public void LoadAction() new ValidatorSet( new List() { new Validator(new PrivateKey().PublicKey, 1) }).Bencoded, - Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value"))); + Dictionary.Empty.Add( + ReservedAddresses.LegacyAccount.ToByteArray(), + Dictionary.Empty.Add( + default(Address).ToByteArray(), "initial value")))); var action = new Initialize(); action.LoadPlainValue(plainValue); diff --git a/Libplanet.Action.Tests/Mocks/MockAccountState.cs b/Libplanet.Action.Tests/Mocks/MockAccountState.cs index 71b8df3cc96..78544f3de9e 100644 --- a/Libplanet.Action.Tests/Mocks/MockAccountState.cs +++ b/Libplanet.Action.Tests/Mocks/MockAccountState.cs @@ -5,6 +5,7 @@ using Libplanet.Store; using Libplanet.Store.Trie; using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; namespace Libplanet.Action.Tests.Mocks @@ -60,8 +61,12 @@ private MockAccountState(ITrie trie) public static MockAccountState Empty => _empty; + public Address Address { get; } + public ITrie Trie { get; } + public BlockHash? BlockHash { get; } + public IValue GetState(Address address) => Trie.Get(KeyConverters.ToStateKey(address)); @@ -95,7 +100,8 @@ public ValidatorSet GetValidatorSet() => : new ValidatorSet(); public MockAccountState SetState(Address address, IValue state) => - new MockAccountState(Trie.Set(KeyConverters.ToStateKey(address), state)); + new MockAccountState( + Trie.Set(KeyConverters.ToStateKey(address), state)); public MockAccountState SetBalance( Address address, FungibleAssetValue amount) => @@ -107,9 +113,10 @@ public MockAccountState SetBalance( public MockAccountState SetBalance( (Address Address, Currency Currency) pair, BigInteger rawAmount) => - new MockAccountState(Trie.Set( - KeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency), - new Integer(rawAmount))); + new MockAccountState( + Trie.Set( + KeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency), + new Integer(rawAmount))); public MockAccountState AddBalance(Address address, FungibleAssetValue amount) => AddBalance((address, amount.Currency), amount.RawValue); @@ -182,8 +189,8 @@ public MockAccountState SubtractTotalSupply(Currency currency, BigInteger rawAmo Integer amount ? amount : 0) - rawAmount); public MockAccountState SetValidator(Validator validator) => - new MockAccountState(Trie.Set( - KeyConverters.ValidatorSetKey, - GetValidatorSet().Update(validator).Bencoded)); + new MockAccountState( + Trie.Set( + KeyConverters.ValidatorSetKey, GetValidatorSet().Update(validator).Bencoded)); } } diff --git a/Libplanet.Action.Tests/Mocks/MockWorldState.cs b/Libplanet.Action.Tests/Mocks/MockWorldState.cs new file mode 100644 index 00000000000..5c4f259c528 --- /dev/null +++ b/Libplanet.Action.Tests/Mocks/MockWorldState.cs @@ -0,0 +1,42 @@ +using System.Collections.Immutable; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Store; +using Libplanet.Store.Trie; + +namespace Libplanet.Action.Tests.Mocks +{ + public class MockWorldState : IWorldState + { + private readonly IImmutableDictionary _accounts; + + public MockWorldState() + : this(ImmutableDictionary.Empty) + { + } + + public MockWorldState(IImmutableDictionary accounts) + : this(new TrieStateStore(new MemoryKeyValueStore()).GetStateRoot(null), accounts) + { + } + + public MockWorldState(ITrie trie, IImmutableDictionary accounts) + { + Trie = trie; + Legacy = true; + _accounts = accounts; + } + + public ITrie Trie { get; } + + public bool Legacy { get; private set; } + + public IAccount GetAccount(Address address) + => _accounts.TryGetValue(address, out IAccount account) + ? account + : new Account(new MockAccountState()); + + public IWorldState SetAccount(Address address, IAccount account) => + new MockWorldState(_accounts.SetItem(address, account)); + } +} diff --git a/Libplanet.Action.Tests/Sys/InitializeTest.cs b/Libplanet.Action.Tests/Sys/InitializeTest.cs index 5c6bea6328c..4bba35ae0ed 100644 --- a/Libplanet.Action.Tests/Sys/InitializeTest.cs +++ b/Libplanet.Action.Tests/Sys/InitializeTest.cs @@ -20,10 +20,15 @@ public class InitializeTest } ); - private static readonly ImmutableDictionary _states = - new Dictionary + private static readonly IImmutableDictionary> + _states = + new Dictionary> { - [default] = (Text)"initial value", + [default] = + new Dictionary + { + [default] = (Text)"initial value", + }.ToImmutableDictionary(), }.ToImmutableDictionary(); [Fact] @@ -42,7 +47,7 @@ public void Execute() { var random = new System.Random(); Address signer = random.NextAddress(); - var prevState = new Account(MockAccountState.Empty); + var prevState = World.Create(new MockWorldState()); BlockHash genesisHash = random.NextBlockHash(); var context = new ActionContext( signer: signer, @@ -61,8 +66,12 @@ public void Execute() var nextState = initialize.Execute(context); - Assert.Equal(_validatorSet, nextState.GetValidatorSet()); - Assert.Equal(_states[default], nextState.GetState(default)); + Assert.Equal( + _validatorSet, + nextState.GetAccount(ReservedAddresses.LegacyAccount).GetValidatorSet()); + Assert.Equal( + _states[default][default], + nextState.GetAccount(default).GetState(default)); } [Fact] @@ -70,7 +79,7 @@ public void ExecuteInNonGenesis() { var random = new System.Random(); Address signer = random.NextAddress(); - var prevState = new Account(MockAccountState.Empty); + var prevState = World.Create(new MockWorldState()); BlockHash genesisHash = random.NextBlockHash(); var context = new ActionContext( signer: signer, @@ -106,7 +115,10 @@ public void Serialize() "values", new List( _validatorSet.Bencoded, - Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value"))), + Dictionary.Empty.Add( + default(Address).ToByteArray(), + Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value" + )))), action.PlainValue); } @@ -119,7 +131,10 @@ public void Deserialize() "values", new List( _validatorSet.Bencoded, - Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value"))); + Dictionary.Empty.Add( + default(Address).ToByteArray(), + Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value" + )))); var action = new Initialize(); action.LoadPlainValue(encoded); diff --git a/Libplanet.Action.Tests/Sys/RegistryTest.cs b/Libplanet.Action.Tests/Sys/RegistryTest.cs index c209d59f8a5..489372d5461 100644 --- a/Libplanet.Action.Tests/Sys/RegistryTest.cs +++ b/Libplanet.Action.Tests/Sys/RegistryTest.cs @@ -19,10 +19,15 @@ public class RegistryTest } ); - private static readonly ImmutableDictionary _states = - new Dictionary + private static readonly IImmutableDictionary> + _states = + new Dictionary> { - [default] = (Text)"initial value", + [default] = + new Dictionary + { + [default] = (Text)"initial value", + }.ToImmutableDictionary(), }.ToImmutableDictionary(); private static readonly Currency FooCurrency = Currency.Uncapped("FOO", 2, null); @@ -36,7 +41,10 @@ public void Deserialize() "values", new List( _validatorSet.Bencoded, - Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value"))); + Dictionary.Empty.Add( + default(Address).ToByteArray(), + Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value" + )))); IAction action = Registry.Deserialize(value); var initialize = Assert.IsType(action); Assert.Equal(_validatorSet, initialize.ValidatorSet); @@ -82,7 +90,10 @@ public void Serialize() "values", new List( _validatorSet.Bencoded, - Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value"))); + Dictionary.Empty.Add( + default(Address).ToByteArray(), + Dictionary.Empty.Add(default(Address).ToByteArray(), "initial value" + )))); TestUtils.AssertBencodexEqual(expected, actual); } diff --git a/Libplanet.Action.Tests/TestUtils.cs b/Libplanet.Action.Tests/TestUtils.cs index 192b0274b71..5f2e4be7a20 100644 --- a/Libplanet.Action.Tests/TestUtils.cs +++ b/Libplanet.Action.Tests/TestUtils.cs @@ -1,11 +1,14 @@ +using System.Collections.Immutable; using Bencodex.Types; using DiffPlex.DiffBuilder; using DiffPlex.DiffBuilder.Model; +using Libplanet.Action.State; +using Xunit; using Xunit.Sdk; namespace Libplanet.Action.Tests { - internal static class TestUtils + public static class TestUtils { private static readonly System.Random _random = new System.Random(); @@ -46,6 +49,61 @@ public static void AssertBencodexEqual(IValue expected, IValue actual) ); } + public static void AssertAccountEqual(IAccount expected, IAccount actual) + { + if (expected is null && actual is null) + { + return; + } + + if (expected is null || actual is null || + !(expected is Account ea && actual is Account aa)) + { + throw new XunitException("Accounts should be of type Account"); + } + + if (!ea.TotalUpdatedFungibleAssets.SequenceEqual(aa.TotalUpdatedFungibleAssets) || + !DictionaryEquals(ea.TotalUpdatedFungibles, aa.TotalUpdatedFungibles)) + { + Assert.Equal(expected, actual); + } + + if (!DictionaryEquals(ea.Delta.ToRawDelta(), aa.Delta.ToRawDelta())) + { + Assert.Equal(ea.Delta, aa.Delta); + } + } + + public static bool DictionaryEquals( + IImmutableDictionary expected, + IImmutableDictionary actual) + { + if (expected is null && actual is null) + { + return true; + } + + if (expected is null || actual is null) + { + return false; + } + + if (expected.Count != actual.Count) + { + return false; + } + + foreach (KeyValuePair pair in expected) + { + if (!actual.TryGetValue(pair.Key, out T2 value) || !pair.Value.Equals(value)) + { + return false; + } + } + + return true; + } + public static byte[] GetRandomBytes(int size) { var bytes = new byte[size]; diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index 3676c912997..7030423f53c 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -215,5 +215,17 @@ FungibleAssetValue GetTotalSupply( /// /// 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.Analyzers.Tests/ActionAnalyzerTest.cs b/Libplanet.Analyzers.Tests/ActionAnalyzerTest.cs index 52eb38179d9..824a7349e05 100644 --- a/Libplanet.Analyzers.Tests/ActionAnalyzerTest.cs +++ b/Libplanet.Analyzers.Tests/ActionAnalyzerTest.cs @@ -31,7 +31,7 @@ public class SampleAction : IAction { public SampleAction() {} public IValue PlainValue => Null.Value; public void LoadPlainValue(IValue plainValue) {} - public IAccount Execute(IActionContext context) { + public IWorld Execute(IActionContext context) { new Random().Next(); return context.PreviousState; } @@ -130,7 +130,7 @@ public class SampleAction : IAction { public SampleAction() {} public IValue PlainValue => Null.Value; public void LoadPlainValue(IValue plainValue) {} - public IAccount Execute(IActionContext context) { + public IWorld Execute(IActionContext context) { // Following code should all fail: var v = " + expr + @"; ConsumeEnumerable(v); diff --git a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs index 8f35c9797cd..1f17ba483fe 100644 --- a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs +++ b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs @@ -102,7 +102,8 @@ public GeneratedBlockChainFixture( new ValidatorSet( ImmutableList.Empty.Add( new Validator(pk.PublicKey, 1)).ToList()), - ImmutableDictionary.Empty), + ImmutableDictionary.Create< + Address, IImmutableDictionary>()) }.ToPlainValues())) .ToImmutableList()); Chain = BlockChain.Create( diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index f2aa3674e5b..dc51ea04159 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Linq; using System.Numerics; +using System.Security.Cryptography; using System.Threading.Tasks; using Bencodex.Types; using GraphQL; @@ -9,25 +10,79 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Blockchain.Policies; +using Libplanet.Common; using Libplanet.Crypto; +using Libplanet.Explorer.Queries; +using Libplanet.Store; +using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; -using Libplanet.Explorer.Queries; -using Libplanet.Store.Trie; using Xunit; using static Libplanet.Explorer.Tests.GraphQLTestUtils; -using Libplanet.Common; -using System.Security.Cryptography; namespace Libplanet.Explorer.Tests.Queries; public class StateQueryTest { + [Fact] + public async Task WorldStates() + { + (IBlockChainStates, IBlockPolicy) source = ( + new MockChainStates(), new BlockPolicy() + ); + ExecutionResult result = await ExecuteQueryAsync(@" + { + worldState( + offsetBlockHash: + ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" + ) + { + stateRootHash + legacy + } + } + ", source: source); + Assert.Null(result.Errors); + ExecutionNode resultData = Assert.IsAssignableFrom(result.Data); + IDictionary resultDict = + Assert.IsAssignableFrom>(resultData!.ToValue()); + IDictionary states = + Assert.IsAssignableFrom>(resultDict["worldState"]); + Assert.NotNull(states["stateRootHash"]); + Assert.True((bool)states["legacy"]); + } + + [Fact] + public async Task AccountStates() + { + (IBlockChainStates, IBlockPolicy) source = ( + new MockChainStates(), new BlockPolicy() + ); + ExecutionResult result = await ExecuteQueryAsync(@" + { + accountState( + accountAddress: ""0x40837BFebC1b192600023a431400557EA5FDE51a"", + offsetBlockHash: + ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" + ) + { + stateRootHash + } + } + ", source: source); + Assert.Null(result.Errors); + ExecutionNode resultData = Assert.IsAssignableFrom(result.Data); + IDictionary resultDict = + Assert.IsAssignableFrom>(resultData!.ToValue()); + IDictionary states = + Assert.IsAssignableFrom>(resultDict["accountState"]); + Assert.NotNull(states["stateRootHash"]); + } + [Fact] public async Task States() { - var currency = Currency.Uncapped("ABC", 2, minters: null); (IBlockChainStates, IBlockPolicy) source = ( new MockChainStates(), new BlockPolicy() ); @@ -35,6 +90,7 @@ public async Task States() { states( addresses: [""0x5003712B63baAB98094aD678EA2B24BcE445D076"", ""0x0000000000000000000000000000000000000000""], + accountAddress: ""0x40837BFebC1b192600023a431400557EA5FDE51a"" offsetBlockHash: ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" ) @@ -93,12 +149,12 @@ public async Task Balance() [Fact] public async Task TotalSupply() { - var currency = Currency.Uncapped("ABC", 2, minters: null); + var currency = Currency.Uncapped("ABC", 2, minters: null); #pragma warning disable CS0618 // Legacy, which is obsolete, is the only way to test this: - var legacyToken = Currency.Legacy("LEG", 0, null); + var legacyToken = Currency.Legacy("LEG", 0, null); #pragma warning restore CS0618 - (IBlockChainStates, IBlockPolicy) source = ( - new MockChainStates(), new BlockPolicy()); + (IBlockChainStates, IBlockPolicy) source = ( + new MockChainStates(), new BlockPolicy()); ExecutionResult result = await ExecuteQueryAsync(@" { totalSupply( @@ -149,10 +205,10 @@ public async Task TotalSupply() [Fact] public async Task Validators() { - (IBlockChainStates, IBlockPolicy) source = ( - new MockChainStates(), - new BlockPolicy() - ); + (IBlockChainStates, IBlockPolicy) source = ( + new MockChainStates(), + new BlockPolicy() + ); ExecutionResult result = await ExecuteQueryAsync(@" { validators( @@ -349,37 +405,38 @@ public async Task ValidatorsBySrh() private class MockChainStates : IBlockChainStates { - public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => - GetAccountState(accountAddress, offset).GetState(address); - public IReadOnlyList GetStates( - IReadOnlyList
addresses, Address accountAddress, BlockHash? offset) => - GetAccountState(accountAddress, offset).GetStates(addresses); + IReadOnlyList
addresses, + HashDigest? stateRootHash) + { + throw new System.NotImplementedException(); + } public FungibleAssetValue GetBalance( - Address address, - Currency currency, - BlockHash? offset) => - GetAccountState(ReservedAddresses.LegacyAccount, offset).GetBalance(address, currency); + Address address, Currency currency, BlockHash? offset) => + new MockAccount().GetBalance(address, currency); public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - GetAccountState(ReservedAddresses.LegacyAccount, offset).GetTotalSupply(currency); + new MockAccount().GetTotalSupply(currency); public ValidatorSet GetValidatorSet(BlockHash? offset) => - GetAccountState(ReservedAddresses.LegacyAccount, offset).GetValidatorSet(); + new MockAccount().GetValidatorSet(); + + public IValue GetState(Address address, Address accountAddress, BlockHash? offset) + => new MockAccount().GetState(address); public IWorldState GetWorldState(BlockHash? offset) { throw new System.NotImplementedException(); } - public IAccountState GetAccountState(Address address, BlockHash? offset) => - new MockAccount(address, null); + public IReadOnlyList GetStates(IReadOnlyList
addresses, Address accountAddress, BlockHash? offset) + => new MockAccount().GetStates(addresses); - public IAccountState GetAccountState(HashDigest? hash) => - new MockAccount(null, hash); + public IAccountState GetAccountState(Address address, BlockHash? blockHash) + => new MockAccount(); - public ITrie GetTrie(BlockHash? offset) + public ITrie GetBlockTrie(BlockHash? offset) { throw new System.NotImplementedException(); } @@ -388,27 +445,75 @@ 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(); } - private class MockAccount : IAccount + private class MockWorld : IWorld { - public MockAccount(Address? address, HashDigest? stateRootHash) + public MockWorld() + { + Trie = new TrieStateStore(new MemoryKeyValueStore()).GetStateRoot(null); + } + public ITrie Trie { get; } + + public bool Legacy => true; + + public IWorldDelta Delta => throw new System.NotImplementedException(); + + public IAccount GetAccount(Address address) + => new MockAccount(); + + public IWorld SetAccount(Address address, IAccount account) { - Address = address ?? default; - StateRootHash = stateRootHash ?? default; + throw new System.NotImplementedException(); } + } - public Address Address { get; } + private class MockAccount : IAccount + { + public MockAccount() + { + Trie = new TrieStateStore(new MemoryKeyValueStore()).GetStateRoot(null); + } public ITrie Trie { get; } - public IAccountDelta Delta { get; } + public IAccountDelta Delta => throw new System.NotImplementedException(); + + public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets => throw new System.NotImplementedException(); + + public IValue GetState(Address address) => GetStates(new[] { address }).First(); + + public IReadOnlyList GetStates(IReadOnlyList
addresses) => + addresses.Select(address => address.ToString() switch + { + "0x5003712B63baAB98094aD678EA2B24BcE445D076" => (IValue)Null.Value, + _ => null, + }).ToImmutableList(); + + public FungibleAssetValue GetBalance(Address address, Currency currency) => + currency * 123; - public BlockHash BlockHash { get; } + public FungibleAssetValue GetTotalSupply(Currency currency) => + currency * 10000; - public HashDigest StateRootHash { get; } + public ValidatorSet GetValidatorSet() => + new ValidatorSet(new List + { + new( + PublicKey.FromHex( + "032038e153d344773986c039ba5dbff12ae70cfdf6ea8beb7c5ea9b361a72a9233"), + new BigInteger(1)), + }); - public IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets { get; } public IAccount SetState(Address address, IValue state) { throw new System.NotImplementedException(); @@ -419,8 +524,7 @@ public IAccount MintAsset(IActionContext context, Address recipient, FungibleAss throw new System.NotImplementedException(); } - public IAccount TransferAsset(IActionContext context, Address sender, Address recipient, - FungibleAssetValue value, bool allowNegativeBalance = false) + public IAccount TransferAsset(IActionContext context, Address sender, Address recipient, FungibleAssetValue value, bool allowNegativeBalance = false) { throw new System.NotImplementedException(); } @@ -434,36 +538,5 @@ public IAccount SetValidator(Validator validator) { throw new System.NotImplementedException(); } - - - public IValue GetState(Address address) => GetStates(new[] { address }).First(); - - public IReadOnlyList GetStates(IReadOnlyList
addresses) => - addresses.Select(address => address.ToString() switch - { - "0x5003712B63baAB98094aD678EA2B24BcE445D076" => (IValue)Null.Value, - _ => null, - }).ToImmutableList(); - - public FungibleAssetValue GetBalance(Address address, Currency currency) => - BlockHash is { } && StateRootHash is { } - ? currency * 123 - : currency * 0; - - public FungibleAssetValue GetTotalSupply(Currency currency) => - BlockHash is { } && StateRootHash is { } - ? currency * 10000 - : currency * 0; - - public ValidatorSet GetValidatorSet() => - BlockHash is { } && StateRootHash is { } - ? new ValidatorSet(new List - { - new( - PublicKey.FromHex( - "032038e153d344773986c039ba5dbff12ae70cfdf6ea8beb7c5ea9b361a72a9233"), - new BigInteger(1)), - }) - : new ValidatorSet(); } } diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs index 3a0bb11ba1e..7eff5eba2d6 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs @@ -75,8 +75,14 @@ public async Task BindSignatureWithSystemAction() var action = new Initialize( new ValidatorSet(new List() { new Validator(new PrivateKey().PublicKey, 1 )}), - new Dictionary - { [default] = (Text)"initial value" }.ToImmutableDictionary()); + new Dictionary> + { + [default] = + new Dictionary + { + [default] = (Text)"initial value", + }.ToImmutableDictionary(), + }.ToImmutableDictionary()); var tx = Transaction.Create( 0L, new PrivateKey(), diff --git a/Libplanet.Explorer.Tests/SimpleAction.cs b/Libplanet.Explorer.Tests/SimpleAction.cs index e75e6c45cf5..be15e6b2775 100644 --- a/Libplanet.Explorer.Tests/SimpleAction.cs +++ b/Libplanet.Explorer.Tests/SimpleAction.cs @@ -23,7 +23,7 @@ public void LoadPlainValue(IValue plainValue) { } - public virtual IAccount Execute(IActionContext context) => context.PreviousState; + public virtual IWorld Execute(IActionContext context) => context.PreviousState; public static SimpleAction GetAction(int seed) => (seed % 10) switch @@ -90,6 +90,6 @@ public class SimpleAction8 : SimpleAction [ActionType(nameof(SimpleAction0Fail))] public class SimpleAction0Fail : SimpleAction { - public override IAccount Execute(IActionContext context) => + public override IWorld Execute(IActionContext context) => throw new CurrencyPermissionException("test message", context.Signer, default); } diff --git a/Libplanet.Explorer/Queries/StateQuery.cs b/Libplanet.Explorer/Queries/StateQuery.cs index 2b8a1c48ab8..c967a5a198a 100644 --- a/Libplanet.Explorer/Queries/StateQuery.cs +++ b/Libplanet.Explorer/Queries/StateQuery.cs @@ -200,9 +200,9 @@ private static object ResolveBalance( } case (_, srh: not null): - return context.Source.ChainStates.GetAccountState( - offsetStateRootHash - ).GetBalance(owner, currency); + return context.Source.ChainStates.GetWorldState(offsetStateRootHash) + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(owner, currency); } } diff --git a/Libplanet.Net.Tests/SwarmTest.Preload.cs b/Libplanet.Net.Tests/SwarmTest.Preload.cs index bc9db0d1c18..e2b11a8cd48 100644 --- a/Libplanet.Net.Tests/SwarmTest.Preload.cs +++ b/Libplanet.Net.Tests/SwarmTest.Preload.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Bencodex.Types; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -105,7 +106,8 @@ public async Task InitialBlockDownloadStates() await receiverSwarm.AddPeersAsync(new[] { minerSwarm.AsPeer }, null); await receiverSwarm.PreloadAsync(); - var state = receiverChain.GetState(address1); + var state = receiverChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address1); Assert.Equal((Text)"foo,bar,baz", state); Assert.Equal(minerChain.BlockHashes, receiverChain.BlockHashes); @@ -743,7 +745,8 @@ public async Task PreloadAsyncCancellation(int cancelAfter) Assert.Equal(blockArray[0], receiverChain.Tip); Assert.Equal( (Text)string.Join(",", Enumerable.Range(0, 5).Select(j => $"Item0.{j}")), - receiverChain.GetState(address) + receiverChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address) ); } else @@ -756,7 +759,8 @@ public async Task PreloadAsyncCancellation(int cancelAfter) string.Join(",", Enumerable.Range(0, 5).Select(j => $"Item{i}.{j}")) ) ), - receiverChain.GetState(address) + receiverChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address) ); } diff --git a/Libplanet.Net.Tests/SwarmTest.cs b/Libplanet.Net.Tests/SwarmTest.cs index 419f35f00ba..5f4ecb5920a 100644 --- a/Libplanet.Net.Tests/SwarmTest.cs +++ b/Libplanet.Net.Tests/SwarmTest.cs @@ -1055,8 +1055,10 @@ public async void RestageTransactionsOnceLocallyMinedAfterReorg(bool restage) keyB, CreateBlockCommit(minerB.BlockChain.Tip)); minerB.BlockChain.Append(blockC, TestUtils.CreateBlockCommit(blockC)); - Assert.Equal((Text)dumbItem, minerA.BlockChain.GetState(targetAddress1)); - Assert.Equal((Text)dumbItem, minerB.BlockChain.GetState(targetAddress2)); + Assert.Equal((Text)dumbItem, minerA.BlockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(targetAddress1)); + Assert.Equal((Text)dumbItem, minerB.BlockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(targetAddress2)); await StartAsync(minerA); await StartAsync(minerB); @@ -1071,8 +1073,10 @@ public async void RestageTransactionsOnceLocallyMinedAfterReorg(bool restage) Assert.Equal(3, minerA.BlockChain.Count); Assert.Equal( restage ? null : (Text?)dumbItem, - minerA.BlockChain.GetState(targetAddress1)); - Assert.Equal((Text)dumbItem, minerA.BlockChain.GetState(targetAddress2)); + minerA.BlockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(targetAddress1)); + Assert.Equal((Text)dumbItem, minerA.BlockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(targetAddress2)); Log.Debug("Check if txs in unrendered blocks staged again"); Assert.Equal( @@ -1086,8 +1090,10 @@ public async void RestageTransactionsOnceLocallyMinedAfterReorg(bool restage) await minerB.BlockAppended.WaitAsync(); Assert.Equal(minerA.BlockChain.Tip, minerB.BlockChain.Tip); - Assert.Equal((Text)dumbItem, minerA.BlockChain.GetState(targetAddress1)); - Assert.Equal((Text)dumbItem, minerA.BlockChain.GetState(targetAddress2)); + Assert.Equal((Text)dumbItem, minerA.BlockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(targetAddress1)); + Assert.Equal((Text)dumbItem, minerA.BlockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(targetAddress2)); } finally { @@ -1395,9 +1401,12 @@ public async Task DoNotReceiveBlockFromNodeHavingDifferenceGenesisBlock() Assert.Equal(1, genesisChainB.Count); Assert.Equal(2, genesisChainC.Count); - Assert.Equal("1", (Text)genesisChainA.GetState(signerAddress)); - Assert.Equal("2", (Text)genesisChainB.GetState(signerAddress)); - Assert.Equal("1", (Text)genesisChainC.GetState(signerAddress)); + Assert.Equal("1", (Text)genesisChainA.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(signerAddress)); + Assert.Equal("2", (Text)genesisChainB.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(signerAddress)); + Assert.Equal("1", (Text)genesisChainC.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(signerAddress)); } finally { @@ -2017,7 +2026,7 @@ private class Sleep : IAction { public IValue PlainValue => Null.Value; - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { Thread.Sleep(10); return context.PreviousState; diff --git a/Libplanet.Tests/Action/AccountDiffTest.cs b/Libplanet.Tests/Action/AccountDiffTest.cs index 16c902ad210..a47fdec6bb6 100644 --- a/Libplanet.Tests/Action/AccountDiffTest.cs +++ b/Libplanet.Tests/Action/AccountDiffTest.cs @@ -40,7 +40,7 @@ public void EmptyAccountStateSource() Assert.Empty(diff.TotalSupplyDiffs); Assert.Null(diff.ValidatorSetDiff); - IAccount targetAccount = new Account(new AccountState(targetTrie)); + IAccount targetAccount = new Account(new AccountBaseState(targetTrie)); PrivateKey signer = new PrivateKey(); IActionContext context = CreateActionContext(signer.ToAddress(), targetTrie); targetAccount = targetAccount.MintAsset( @@ -73,7 +73,7 @@ public void Diff() Assert.Empty(diff.TotalSupplyDiffs); Assert.Null(diff.ValidatorSetDiff); - IAccount targetAccount = new Account(new AccountState(targetTrie)); + IAccount targetAccount = new Account(new AccountBaseState(targetTrie)); PrivateKey signer = new PrivateKey(); IActionContext context = CreateActionContext(signer.ToAddress(), targetTrie); targetAccount = targetAccount.SetState(addr1, new Text("One")); @@ -84,7 +84,7 @@ public void Diff() targetTrie = Commit(stateStore, targetTrie, targetAccount.Delta); sourceTrie = targetTrie; - IAccount sourceAccount = new Account(new AccountState(sourceTrie)); + IAccount sourceAccount = new Account(new AccountBaseState(sourceTrie)); sourceAccount = sourceAccount.SetState(addr2, new Text("Two_")); sourceAccount = sourceAccount.SetState(addr3, new Text("Three")); sourceAccount = sourceAccount.MintAsset( @@ -148,7 +148,10 @@ public IActionContext CreateActionContext(Address signer, ITrie trie) => signer, 0, Block.CurrentProtocolVersion, - new Account(new AccountState(trie)), + new World( + new WorldBaseState( + trie, + new TrieStateStore(new MemoryKeyValueStore()))), 0, 0, false); diff --git a/Libplanet.Tests/Action/AccountV0Test.cs b/Libplanet.Tests/Action/AccountV0Test.cs index b17714c7fce..d7f12211aee 100644 --- a/Libplanet.Tests/Action/AccountV0Test.cs +++ b/Libplanet.Tests/Action/AccountV0Test.cs @@ -1,6 +1,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Action.Tests.Common; +using Libplanet.Action.Tests.Mocks; using Libplanet.Blockchain; using Libplanet.Crypto; using Libplanet.Types.Blocks; @@ -12,6 +13,9 @@ namespace Libplanet.Tests.Action { public class AccountV0Test : AccountTest { + private readonly Address _accountAddress + = new Address("2000000000000000000000000000000000000000"); + public AccountV0Test(ITestOutputHelper output) : base(output) { @@ -20,16 +24,20 @@ public AccountV0Test(ITestOutputHelper output) public override int ProtocolVersion { get; } = 0; public override IActionContext CreateContext( - IAccount delta, Address signer) => - new ActionContext( + IAccount delta, Address signer) + { + IWorld world = World.Create(new MockWorldState()); + world = world.SetAccount(_accountAddress, delta); + return new ActionContext( signer, null, signer, 0, ProtocolVersion, - delta, + world, 0, 0); + } [Fact] public override void TransferAsset() diff --git a/Libplanet.Tests/Action/AccountV1Test.cs b/Libplanet.Tests/Action/AccountV1Test.cs index c646dcb7f51..126e2ecc8ca 100644 --- a/Libplanet.Tests/Action/AccountV1Test.cs +++ b/Libplanet.Tests/Action/AccountV1Test.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Action.Tests.Common; +using Libplanet.Action.Tests.Mocks; using Libplanet.Blockchain; using Libplanet.Crypto; using Libplanet.Types.Assets; @@ -15,6 +16,9 @@ namespace Libplanet.Tests.Action { public class AccountV1Test : AccountTest { + private readonly Address _accountAddress + = new Address("2000000000000000000000000000000000000000"); + public AccountV1Test(ITestOutputHelper output) : base(output) { @@ -22,17 +26,20 @@ public AccountV1Test(ITestOutputHelper output) public override int ProtocolVersion { get; } = Block.CurrentProtocolVersion; - public override IActionContext CreateContext( - IAccount delta, Address signer) => - new ActionContext( + public override IActionContext CreateContext(IAccount delta, Address signer) + { + IWorld world = World.Create(new MockWorldState()); + world = world.SetAccount(_accountAddress, delta); + return new ActionContext( signer, null, signer, 0, ProtocolVersion, - delta, + world, 0, 0); + } [Fact] public override void TransferAsset() diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index ca5f9d2eb08..8f7fbad592e 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -102,13 +102,15 @@ public void Idempotent() { var actionEvaluations = actionEvaluator.Evaluate(noStateRootBlock, null); generatedRandomNumbers.Add( - (Integer)new AccountState( - stateStore.GetStateRoot(actionEvaluations[0].OutputState)) + (Integer)new WorldBaseState( + stateStore.GetStateRoot(actionEvaluations[0].OutputState), stateStore) + .GetAccount(ReservedAddresses.LegacyAccount) .GetState(txAddress)); actionEvaluations = actionEvaluator.Evaluate(stateRootBlock, null); generatedRandomNumbers.Add( - (Integer)new AccountState( - stateStore.GetStateRoot(actionEvaluations[0].OutputState)) + (Integer)new WorldBaseState( + stateStore.GetStateRoot(actionEvaluations[0].OutputState), stateStore) + .GetAccount(ReservedAddresses.LegacyAccount) .GetState(txAddress)); } @@ -155,9 +157,13 @@ public void Evaluate() Assert.False(evaluations[0].InputContext.BlockAction); Assert.Single(evaluations); Assert.Null(evaluations.Single().Exception); - Assert.Equal(chain.GetState(action.SignerKey), (Text)address.ToHex()); - Assert.Equal(chain.GetState(action.MinerKey), (Text)miner.ToAddress().ToHex()); - var state = chain.GetState(action.BlockIndexKey); + Assert.Equal( + chain.GetState(action.SignerKey, ReservedAddresses.LegacyAccount), + (Text)address.ToHex()); + Assert.Equal( + chain.GetState(action.MinerKey, ReservedAddresses.LegacyAccount), + (Text)miner.ToAddress().ToHex()); + var state = chain.GetState(action.BlockIndexKey, ReservedAddresses.LegacyAccount); Assert.Equal((long)(Integer)state, blockIndex); } @@ -234,7 +240,7 @@ public void EvaluateWithCriticalException() txHash: BlockContent.DeriveTxHash(txs), lastCommit: null), transactions: txs).Propose(); - IAccount previousState = actionEvaluator.PrepareInitialDelta(genesis.StateRootHash); + IWorld previousState = actionEvaluator.PrepareInitialDelta(genesis.StateRootHash); Assert.Throws( () => actionEvaluator.EvaluateTx( @@ -315,7 +321,7 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu genesis, GenesisProposer, block1Txs); - IAccount previousState = actionEvaluator.PrepareInitialDelta(null); + IWorld previousState = actionEvaluator.PrepareInitialDelta(null); var evals = actionEvaluator.EvaluateBlock( block1, previousState).ToImmutableArray(); @@ -340,12 +346,14 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu Assert.Equal(block1.Index, eval.InputContext.BlockIndex); randomValue = eval.InputContext.GetRandom().Next(); Assert.Equal( - (Integer)eval.OutputState.GetState( - DumbAction.RandomRecordsAddress), + (Integer)eval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(DumbAction.RandomRecordsAddress), (Integer)randomValue); Assert.Equal( expect.UpdatedStates, - addresses.Select(eval.OutputState.GetState) + addresses.Select( + eval.OutputState.GetAccount(ReservedAddresses.LegacyAccount).GetState) .Select(x => x is Text t ? t.Value : null)); } @@ -449,7 +457,7 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu foreach (var (expect, eval) in expectations.Zip(evals, (x, y) => (x, y))) { List updatedStates = addresses - .Select(eval.OutputState.GetState) + .Select(eval.OutputState.GetAccount(ReservedAddresses.LegacyAccount).GetState) .Select(x => x is Text t ? t.Value : null) .ToList(); Assert.Equal(expect.UpdatedStates, updatedStates); @@ -464,8 +472,9 @@ DumbAction MakeAction(Address address, char identifier, Address? transferTo = nu Assert.Null(eval.Exception); randomValue = eval.InputContext.GetRandom().Next(); Assert.Equal( - eval.OutputState.GetState( - DumbAction.RandomRecordsAddress), + eval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(DumbAction.RandomRecordsAddress), (Integer)randomValue); } @@ -534,7 +543,7 @@ public void EvaluateTx() DumbAction.RehearsalRecords.Value = ImmutableList<(Address, string)>.Empty; - IAccount previousState = actionEvaluator.PrepareInitialDelta(null); + IWorld previousState = actionEvaluator.PrepareInitialDelta(null); var evaluations = actionEvaluator.EvaluateTx( blockHeader: block, tx: tx, @@ -568,30 +577,44 @@ public void EvaluateTx() Assert.Equal(addresses[0], eval.InputContext.Miner); Assert.Equal(1, eval.InputContext.BlockIndex); Assert.Equal( - (Integer)eval.OutputState.GetState( - DumbAction.RandomRecordsAddress), + (Integer)eval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState(DumbAction.RandomRecordsAddress), (Integer)eval.InputContext.GetRandom().Next()); IActionEvaluation prevEval = i > 0 ? evaluations[i - 1] : null; Assert.Equal( prevEval is null ? initStates - : addresses.Select(prevEval.OutputState.GetState), - addresses.Select(eval.InputContext.PreviousState.GetState)); + : addresses.Select( + prevEval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState), + addresses.Select( + eval.InputContext.PreviousState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState)); Assert.Equal( expectedStates[i], - addresses.Select(eval.OutputState.GetState) + addresses.Select(eval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetState) .Select(x => x is Text t ? t.Value : null)); Assert.Equal( prevEval is null ? initBalances : addresses.Select(a => - prevEval.OutputState.GetBalance(a, currency).RawValue), + prevEval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(a, currency).RawValue), addresses.Select( a => eval.InputContext.PreviousState + .GetAccount(ReservedAddresses.LegacyAccount) .GetBalance(a, currency).RawValue)); Assert.Equal( expectedBalances[i], - addresses.Select(a => eval.OutputState.GetBalance(a, currency).RawValue)); + addresses.Select(a => eval.OutputState + .GetAccount(ReservedAddresses.LegacyAccount) + .GetBalance(a, currency).RawValue)); } Assert.DoesNotContain( @@ -601,13 +624,14 @@ prevEval is null DumbAction.RehearsalRecords.Value = ImmutableList<(Address, string)>.Empty; previousState = actionEvaluator.PrepareInitialDelta(null); - IAccount delta = actionEvaluator.EvaluateTx( + IWorld delta = actionEvaluator.EvaluateTx( blockHeader: block, tx: tx, previousState: previousState).Last().OutputState; Assert.Equal( - evaluations[3].OutputState.GetUpdatedStates(), - delta.GetUpdatedStates()); + evaluations[3].OutputState + .GetAccount(ReservedAddresses.LegacyAccount).GetUpdatedStates(), + delta.GetAccount(ReservedAddresses.LegacyAccount).GetUpdatedStates()); Assert.DoesNotContain( (addresses[2], "R"), @@ -643,13 +667,13 @@ public void EvaluateTxResultThrowingException() txHash: BlockContent.DeriveTxHash(txs), lastCommit: CreateBlockCommit(hash, 122, 0)), transactions: txs).Propose(); - IAccount previousState = actionEvaluator.PrepareInitialDelta(null); + IWorld previousState = actionEvaluator.PrepareInitialDelta(null); var nextState = actionEvaluator.EvaluateTx( blockHeader: block, tx: tx, previousState: previousState).Last().OutputState; - Assert.Empty(nextState.GetUpdatedStates()); + Assert.Empty(nextState.GetAccount(ReservedAddresses.LegacyAccount).GetUpdatedStates()); } [Fact] @@ -669,7 +693,7 @@ public void EvaluateActions() ActionEvaluation[] evalsA = ActionEvaluator.EvaluateActions( blockHeader: blockA, tx: txA, - previousState: fx.CreateAccount(0, blockA.PreviousHash), + previousState: fx.CreateWorld(blockA.PreviousHash), actions: txA.Actions .Select(action => (IAction)ToAction(action)) .ToImmutableArray()).ToArray(); @@ -677,14 +701,15 @@ public void EvaluateActions() Assert.Equal(evalsA.Length, deltaA.Count - 1); Assert.Equal( new Integer(15), - evalsA.Last().OutputState.GetState(txA.Signer)); + evalsA.Last().OutputState + .GetAccount(ReservedAddresses.LegacyAccount).GetState(txA.Signer)); for (int i = 0; i < evalsA.Length; i++) { IActionEvaluation eval = evalsA[i]; IActionContext context = eval.InputContext; - IAccount prevState = context.PreviousState; - IAccount outputState = eval.OutputState; + IWorld prevState = context.PreviousState; + IWorld outputState = eval.OutputState; _logger.Debug("evalsA[{0}] = {1}", i, eval); _logger.Debug("txA.Actions[{0}] = {1}", i, txA.Actions[i]); @@ -696,10 +721,16 @@ public void EvaluateActions() Assert.False(context.BlockAction); Assert.Equal( i > 0 ? new[] { txA.Signer } : new Address[0], - prevState.Delta.UpdatedAddresses); - Assert.Equal((Integer)deltaA[i].Value, prevState.GetState(txA.Signer)); - Assert.Equal(new[] { txA.Signer }, outputState.Delta.UpdatedAddresses); - Assert.Equal((Integer)deltaA[i + 1].Value, outputState.GetState(txA.Signer)); + prevState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); + Assert.Equal( + (Integer)deltaA[i].Value, + prevState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txA.Signer)); + Assert.Equal( + new[] { txA.Signer }, + outputState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); + Assert.Equal( + (Integer)deltaA[i + 1].Value, + outputState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txA.Signer)); Assert.Null(eval.Exception); } @@ -716,7 +747,7 @@ public void EvaluateActions() ActionEvaluation[] evalsB = ActionEvaluator.EvaluateActions( blockHeader: blockB, tx: txB, - previousState: fx.CreateAccount(0, blockB.PreviousHash), + previousState: fx.CreateWorld(blockB.PreviousHash), actions: txB.Actions .Select(action => (IAction)ToAction(action)) .ToImmutableArray()).ToArray(); @@ -724,14 +755,15 @@ public void EvaluateActions() Assert.Equal(evalsB.Length, deltaB.Count - 1); Assert.Equal( new Integer(6), - evalsB.Last().OutputState.GetState(txB.Signer)); + evalsB.Last().OutputState + .GetAccount(ReservedAddresses.LegacyAccount).GetState(txB.Signer)); for (int i = 0; i < evalsB.Length; i++) { IActionEvaluation eval = evalsB[i]; IActionContext context = eval.InputContext; - IAccount prevState = context.PreviousState; - IAccount outputState = eval.OutputState; + IWorld prevState = context.PreviousState; + IWorld outputState = eval.OutputState; _logger.Debug("evalsB[{0}] = {@1}", i, eval); _logger.Debug("txB.Actions[{0}] = {@1}", i, txB.Actions[i]); @@ -744,10 +776,16 @@ public void EvaluateActions() Assert.False(context.BlockAction); Assert.Equal( i > 0 ? new[] { txB.Signer } : new Address[0], - prevState.Delta.UpdatedAddresses); - Assert.Equal((Integer)deltaB[i].Value, prevState.GetState(txB.Signer)); - Assert.Equal(new[] { txB.Signer }, outputState.Delta.UpdatedAddresses); - Assert.Equal((Integer)deltaB[i + 1].Value, outputState.GetState(txB.Signer)); + prevState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); + Assert.Equal( + (Integer)deltaB[i].Value, + prevState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txB.Signer)); + Assert.Equal( + new[] { txB.Signer }, + outputState.GetAccount(ReservedAddresses.LegacyAccount).Delta.UpdatedAddresses); + Assert.Equal( + (Integer)deltaB[i + 1].Value, + outputState.GetAccount(ReservedAddresses.LegacyAccount).GetState(txB.Signer)); if (i == 1) { Assert.IsType(eval.Exception); @@ -774,13 +812,14 @@ public void EvaluatePolicyBlockAction() var block = chain.ProposeBlock( GenesisProposer, txs.ToImmutableList(), CreateBlockCommit(chain.Tip)); - IAccount previousState = actionEvaluator.PrepareInitialDelta(null); + IWorld previousState = actionEvaluator.PrepareInitialDelta(null); var evaluation = actionEvaluator.EvaluatePolicyBlockAction(genesis, previousState); Assert.Equal(chain.Policy.BlockAction, evaluation.Action); Assert.Equal( (Integer)1, - (Integer)evaluation.OutputState.GetState(genesis.Miner)); + (Integer)evaluation.OutputState + .GetAccount(ReservedAddresses.LegacyAccount).GetState(genesis.Miner)); Assert.True(evaluation.InputContext.BlockAction); previousState = evaluation.OutputState; @@ -789,7 +828,8 @@ public void EvaluatePolicyBlockAction() Assert.Equal(chain.Policy.BlockAction, evaluation.Action); Assert.Equal( (Integer)2, - (Integer)evaluation.OutputState.GetState(block.Miner)); + (Integer)evaluation.OutputState + .GetAccount(ReservedAddresses.LegacyAccount).GetState(block.Miner)); Assert.True(evaluation.InputContext.BlockAction); chain.Append(block, CreateBlockCommit(block), render: true); @@ -802,7 +842,8 @@ public void EvaluatePolicyBlockAction() Assert.Equal( (Integer)2, - (Integer)evaluation.OutputState.GetState(block.Miner)); + (Integer)evaluation.OutputState + .GetAccount(ReservedAddresses.LegacyAccount).GetState(block.Miner)); } [Theory] @@ -926,19 +967,29 @@ public void TotalUpdatedFungibleAssets() var evals = actionEvaluator.EvaluateBlock( block, - new Account(chain.GetAccountState(block.PreviousHash))); + new World(chain.GetWorldState(block.PreviousHash))); // Does not include policy block action Assert.Equal(3, evals.Count()); var latest = evals.Last().OutputState; // Only addresses[0] and addresses[1] succeeded in minting - Assert.Equal(2, latest.TotalUpdatedFungibleAssets.Count); - Assert.Contains((addresses[0], currency), latest.TotalUpdatedFungibleAssets); - Assert.Contains((addresses[1], currency), latest.TotalUpdatedFungibleAssets); + Assert.Equal( + 2, + latest + .GetAccount(ReservedAddresses.LegacyAccount).TotalUpdatedFungibleAssets.Count); + Assert.Contains( + (addresses[0], currency), + latest + .GetAccount(ReservedAddresses.LegacyAccount).TotalUpdatedFungibleAssets); + Assert.Contains( + (addresses[1], currency), + latest.GetAccount(ReservedAddresses.LegacyAccount).TotalUpdatedFungibleAssets); Assert.DoesNotContain( addresses[2], - latest.TotalUpdatedFungibleAssets.Select(pair => pair.Item1)); + latest + .GetAccount(ReservedAddresses.LegacyAccount) + .TotalUpdatedFungibleAssets.Select(pair => pair.Item1)); } [Fact] @@ -1289,7 +1340,7 @@ private void CheckRandomSeedInAction() ActionEvaluation[] evalsA = ActionEvaluator.EvaluateActions( blockHeader: blockA, tx: txA, - previousState: fx.CreateAccount(0, blockA.PreviousHash), + previousState: fx.CreateWorld(blockA.PreviousHash), actions: txA.Actions .Select(action => (IAction)ToAction(action)) .ToImmutableArray()).ToArray(); @@ -1336,11 +1387,14 @@ public void LoadPlainValue(IValue plainValue) BlockIndexKey = new Address(asList[2]); } - public IAccount Execute(IActionContext context) => + public IWorld Execute(IActionContext context) => context.PreviousState - .SetState(SignerKey, (Text)context.Signer.ToHex()) - .SetState(MinerKey, (Text)context.Miner.ToHex()) - .SetState(BlockIndexKey, (Integer)context.BlockIndex); + .SetAccount( + ReservedAddresses.LegacyAccount, + context.PreviousState.GetAccount(ReservedAddresses.LegacyAccount) + .SetState(SignerKey, (Text)context.Signer.ToHex()) + .SetState(MinerKey, (Text)context.Miner.ToHex()) + .SetState(BlockIndexKey, (Integer)context.BlockIndex)); } private sealed class UseGasAction : IAction @@ -1376,14 +1430,20 @@ public void LoadPlainValue(IValue plainValue) } } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { context.UseGas(GasUsage); var state = context.PreviousState - .SetState(context.Signer, (Text)Memo); + .SetAccount( + ReservedAddresses.LegacyAccount, + context.PreviousState.GetAccount(ReservedAddresses.LegacyAccount) + .SetState(context.Signer, (Text)Memo)); if (!(Receiver is null) && !(MintValue is null)) { - state = state.MintAsset(context, Receiver.Value, MintValue.Value); + state = state.SetAccount( + ReservedAddresses.LegacyAccount, + state.GetAccount(ReservedAddresses.LegacyAccount) + .MintAsset(context, Receiver.Value, MintValue.Value)); } return state; @@ -1401,9 +1461,12 @@ public void LoadPlainValue(IValue plainValue) Currency = new Currency(plainValue); } - public IAccount Execute(IActionContext context) => - context.PreviousState.MintAsset( - context, context.Signer, FungibleAssetValue.FromRawValue(Currency, 1)); + public IWorld Execute(IActionContext context) => + context.PreviousState.SetAccount( + ReservedAddresses.LegacyAccount, + context.PreviousState.GetAccount(ReservedAddresses.LegacyAccount) + .MintAsset( + context, context.Signer, FungibleAssetValue.FromRawValue(Currency, 1))); } } diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index e4ace3c7e92..57af325e63e 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -81,47 +81,62 @@ Func getTxExecution Assert.Equal( new IValue[] { null, null, null, null, (Integer)1 }, addresses.Select( - _blockChain.GetAccountState(renders[0].Context.PreviousState).GetState) + _blockChain.GetWorldState(renders[0].Context.PreviousState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", null, null, null, (Integer)1 }, - addresses.Select(_blockChain.GetAccountState(renders[0].NextState).GetState) + addresses.Select( + _blockChain.GetWorldState(renders[0].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal("bar", actions[1].Item); Assert.Equal(2, renders[1].Context.BlockIndex); Assert.Equal( - addresses.Select(_blockChain.GetAccountState(renders[0].NextState).GetState), - addresses.Select( - _blockChain.GetAccountState(renders[1].Context.PreviousState).GetState) + addresses.Select(_blockChain.GetWorldState(renders[0].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState), + addresses.Select(_blockChain.GetWorldState(renders[1].Context.PreviousState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", null, null, (Integer)1 }, - addresses.Select(_blockChain.GetAccountState(renders[1].NextState).GetState) + addresses.Select( + _blockChain.GetWorldState(renders[1].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal("baz", actions[2].Item); Assert.Equal(2, renders[2].Context.BlockIndex); Assert.Equal( - addresses.Select(_blockChain.GetAccountState(renders[1].NextState).GetState), addresses.Select( - _blockChain.GetAccountState(renders[2].Context.PreviousState).GetState) + _blockChain.GetWorldState(renders[1].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState), + addresses.Select( + _blockChain.GetWorldState(renders[2].Context.PreviousState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", (Text)"baz", null, (Integer)1 }, - addresses.Select(_blockChain.GetAccountState(renders[2].NextState).GetState) + addresses.Select( + _blockChain.GetWorldState(renders[2].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal("qux", actions[3].Item); Assert.Equal(2, renders[3].Context.BlockIndex); Assert.Equal( - addresses.Select(_blockChain.GetAccountState(renders[2].NextState).GetState), addresses.Select( - _blockChain.GetAccountState(renders[3].Context.PreviousState).GetState) + _blockChain.GetWorldState(renders[2].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState), + addresses.Select( + _blockChain.GetWorldState(renders[3].Context.PreviousState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState) ); Assert.Equal( new IValue[] { (Text)"foo", (Text)"bar", (Text)"baz", (Text)"qux", (Integer)1, }, - _blockChain.GetAccountState(renders[3].NextState).GetStates(addresses) + _blockChain.GetWorldState(renders[3].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetStates(addresses) ); Address minerAddress = addresses[4]; @@ -129,7 +144,8 @@ Func getTxExecution .Where(r => TestUtils.IsMinerReward(r.Action)) .ToArray(); - Assert.Equal((Integer)2, (Integer)_blockChain.GetState(minerAddress)); + Assert.Equal((Integer)2, (Integer)_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(minerAddress)); Assert.Equal(2, blockRenders.Length); Assert.True(blockRenders.All(r => r.Render)); Assert.Equal(1, blockRenders[0].Context.BlockIndex); @@ -137,18 +153,18 @@ Func getTxExecution Assert.Equal( (Integer)1, - (Integer)_blockChain.GetAccountState( - blockRenders[0].NextState).GetState(minerAddress) + (Integer)_blockChain.GetWorldState(blockRenders[0].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState(minerAddress) ); Assert.Equal( (Integer)1, - (Integer)_blockChain.GetAccountState(blockRenders[1].Context.PreviousState) - .GetState(minerAddress) + (Integer)_blockChain.GetWorldState(blockRenders[1].Context.PreviousState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState(minerAddress) ); Assert.Equal( (Integer)2, - (Integer)_blockChain.GetAccountState( - blockRenders[1].NextState).GetState(minerAddress) + (Integer)_blockChain.GetWorldState(blockRenders[1].NextState) + .GetAccount(ReservedAddresses.LegacyAccount).GetState(minerAddress) ); foreach (Transaction tx in txs) @@ -161,9 +177,11 @@ Func getTxExecution Assert.Equal(block2.Hash, e.BlockHash); Assert.Equal(tx.Id, e.TxId); var inputAccount = - _blockChain.GetAccountState(Assert.IsType>(e.InputState)); + _blockChain.GetWorldState(Assert.IsType>(e.InputState)) + .GetAccount(ReservedAddresses.LegacyAccount); var outputAccount = - _blockChain.GetAccountState(Assert.IsType>(e.OutputState)); + _blockChain.GetWorldState(Assert.IsType>(e.OutputState)) + .GetAccount(ReservedAddresses.LegacyAccount); var accountDiff = AccountDiff.Create(inputAccount, outputAccount); Assert.Empty(accountDiff.FungibleAssetValueDiffs); } @@ -256,6 +274,44 @@ Func getTxExecution outputAccount3.GetBalance(addresses[1], DumbAction.DumbCurrency)); } + [SkippableFact] + public void AppendModern() + { + _blockChain = TestUtils.MakeBlockChain( + new NullBlockPolicy(), + new MemoryStore(), + new TrieStateStore(new MemoryKeyValueStore())); + var genesis = _blockChain.Genesis; + var address1 = new Address(TestUtils.GetRandomBytes(20)); + var address2 = new Address(TestUtils.GetRandomBytes(20)); + var miner = new PrivateKey(); + var action1 = new DumbModernAction(address1, "foo"); + var action2 = new DumbModernAction(address2, "bar"); + var tx1 = Transaction.Create(0, miner, genesis.Hash, new[] { action1 }.ToPlainValues()); + var tx2 = Transaction.Create(1, miner, genesis.Hash, new[] { action2 }.ToPlainValues()); + var block1 = _blockChain.ProposeBlock( + miner, + new[] { tx1 }.ToImmutableList(), + TestUtils.CreateBlockCommit(_blockChain.Tip)); + var commit1 = TestUtils.CreateBlockCommit(block1); + _blockChain.Append(block1, commit1); + Assert.Equal( + (Text)"foo", + _blockChain.GetWorldState() + .GetAccount(DumbModernAction.DumbModernAddress) + .GetState(address1)); + var block2 = _blockChain.ProposeBlock( + miner, + new[] { tx2 }.ToImmutableList(), + commit1); + _blockChain.Append(block2, TestUtils.CreateBlockCommit(block2)); + Assert.Equal( + (Text)"bar", + _blockChain.GetWorldState() + .GetAccount(DumbModernAction.DumbModernAddress) + .GetState(address2)); + } + [SkippableFact] public void AppendFailDueToInvalidBytesLength() { diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs index 03aa45dbc77..74b907f42d7 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ProposeBlock.cs @@ -5,6 +5,7 @@ using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -28,7 +29,8 @@ public void ProposeBlock() Assert.Equal(1, _blockChain.Count); AssertBencodexEqual( (Text)$"{GenesisProposer.ToAddress()}", - _blockChain.GetState(default)); + _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(default)); var proposerA = new PrivateKey(); Block block = _blockChain.ProposeBlock(proposerA); @@ -39,7 +41,8 @@ public void ProposeBlock() block.MarshalBlock().EncodingLength <= getMaxTransactionsBytes(block.Index)); AssertBencodexEqual( (Text)$"{GenesisProposer.ToAddress()},{proposerA.ToAddress()}", - _blockChain.GetState(default) + _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(default) ); var proposerB = new PrivateKey(); @@ -56,7 +59,8 @@ public void ProposeBlock() $"{GenesisProposer.ToAddress()},{proposerA.ToAddress()},{proposerB.ToAddress()}"); AssertBencodexEqual( expected, - _blockChain.GetState(default) + _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(default) ); Block block3 = _blockChain.ProposeBlock( @@ -70,7 +74,8 @@ public void ProposeBlock() $"{GenesisProposer.ToAddress()},{proposerA.ToAddress()},{proposerB.ToAddress()}"); AssertBencodexEqual( expected, - _blockChain.GetState(default) + _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(default) ); // Tests if ProposeBlock() method automatically fits the number of transactions @@ -113,7 +118,8 @@ public void ProposeBlock() $"{GenesisProposer.ToAddress()},{proposerA.ToAddress()},{proposerB.ToAddress()}"); AssertBencodexEqual( expected, - _blockChain.GetState(default) + _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(default) ); } @@ -270,11 +276,16 @@ public void ProposeBlockWithPendingTxs() StageTransactions(txs); - Assert.Null(_blockChain.GetState(addrA)); - Assert.Null(_blockChain.GetState(addrB)); - Assert.Null(_blockChain.GetState(addrC)); - Assert.Null(_blockChain.GetState(addrD)); - Assert.Null(_blockChain.GetState(addrE)); + Assert.Null(_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrA)); + Assert.Null(_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrB)); + Assert.Null(_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrC)); + Assert.Null(_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrD)); + Assert.Null(_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrE)); foreach (Transaction tx in txs) { @@ -295,15 +306,22 @@ public void ProposeBlockWithPendingTxs() Assert.Contains(txs[2].Id, txIds); Assert.Contains(txs[3].Id, txIds); - Assert.Equal(new Integer(1), _blockChain.GetState(addrA)); - Assert.Equal(new Text("1b"), _blockChain.GetState(addrB)); - Assert.Equal(new Text("2a"), _blockChain.GetState(addrC)); - Assert.IsType(_blockChain.GetState(addrD)); + Assert.Equal(new Integer(1), _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrA)); + Assert.Equal(new Text("1b"), _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrB)); + Assert.Equal(new Text("2a"), _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrC)); + Assert.IsType(_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrD)); Assert.Equal( new HashSet { "2b", "5a" }, - ((string)(Text)_blockChain.GetState(addrD)).Split(new[] { ',' }).ToHashSet() + ((string)(Text)_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrD)).Split(new[] { ',' }) + .ToHashSet() ); - Assert.Equal(new Text("5b"), _blockChain.GetState(addrE)); + Assert.Equal(new Text("5b"), _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addrE)); foreach (Transaction tx in new[] { txs[0], txs[1], txs[4] }) { @@ -464,8 +482,10 @@ public void ProposeBlockWithBlockAction() var block = blockChain.ProposeBlock(privateKey1, CreateBlockCommit(_blockChain.Tip)); blockChain.Append(block, CreateBlockCommit(block)); - var state1 = blockChain.GetState(address1); - var state2 = blockChain.GetState(address2); + var state1 = blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address1); + var state2 = blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address2); Assert.Equal(0, blockChain.GetNextTxNonce(address1)); Assert.Equal(1, blockChain.GetNextTxNonce(address2)); @@ -476,8 +496,10 @@ public void ProposeBlockWithBlockAction() block = blockChain.ProposeBlock(privateKey1, CreateBlockCommit(_blockChain.Tip)); blockChain.Append(block, CreateBlockCommit(block)); - state1 = blockChain.GetState(address1); - state2 = blockChain.GetState(address2); + state1 = blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address1); + state2 = blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address2); Assert.Equal(1, blockChain.GetNextTxNonce(address1)); Assert.Equal(1, blockChain.GetNextTxNonce(address2)); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs index a2aa00fa8f6..35159affd0a 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs @@ -213,7 +213,7 @@ public void ValidateNextBlockInvalidStateRootHash() TestUtils.GenesisProposer); var policyWithBlockAction = new BlockPolicy( - new SetStatesAtBlock(default, (Text)"foo", 1), + new SetStatesAtBlock(default, (Text)"foo", default, 1), policy.BlockInterval ); var chain2 = new BlockChain( diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index 83a6e56ce8a..10c1e05e0b7 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -193,7 +193,8 @@ public void ProcessActions() { new Initialize( validatorSet: TestUtils.ValidatorSet, - states: ImmutableDictionary.Create()), + states: ImmutableDictionary.Create + >()), }.ToPlainValues(), timestamp: DateTimeOffset.UtcNow)) .OrderBy(tx => tx.Id) @@ -239,7 +240,8 @@ public void ProcessActions() chain.StageTransaction(tx1); Block block1 = chain.ProposeBlock(new PrivateKey()); chain.Append(block1, CreateBlockCommit(block1)); - IValue state = chain.GetState(_fx.Address1); + IValue state = chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(_fx.Address1); Assert.NotNull(state); var result = BattleResult.FromBencodex((Bencodex.Types.Dictionary)state); @@ -269,7 +271,8 @@ public void ProcessActions() new PrivateKey(), CreateBlockCommit(chain.Tip)); chain.Append(block2, CreateBlockCommit(block2)); - state = chain.GetState(_fx.Address1); + state = chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(_fx.Address1); result = BattleResult.FromBencodex((Bencodex.Types.Dictionary)state); Assert.Contains("bow", result.UsedWeapons); @@ -291,7 +294,8 @@ public void ProcessActions() new PrivateKey(), CreateBlockCommit(chain.Tip)); chain.StageTransaction(tx3); chain.Append(block3, CreateBlockCommit(block3)); - state = chain.GetState(_fx.Address1); + state = chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(_fx.Address1); Assert.NotNull(state); } @@ -568,16 +572,19 @@ public void StateAfterForkingAndAddingExistingBlock() var b2 = _blockChain.ProposeBlock( miner, lastCommit: CreateBlockCommit(_blockChain.Tip)); _blockChain.Append(b2, CreateBlockCommit(b2)); - var state = _blockChain.GetState(address); + var state = _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address); Assert.Equal((Text)"foo,bar", state); var forked = _blockChain.Fork(b1.Hash); - state = forked.GetState(address); + state = forked.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address); Assert.Equal((Text)"foo", state); forked.Append(b2, CreateBlockCommit(b2)); - state = forked.GetState(address); + state = forked.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address); Assert.Equal((Text)"foo,bar", state); } @@ -911,7 +918,8 @@ public void Swap(bool render) // except genesis block. Assert.Equal( (Integer)(totalBlockCount - 1), - (Integer)_blockChain.GetState(minerAddress) + (Integer)_blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(minerAddress) ); Assert.Single(blockActionRenders); // #1 -> #2' Assert.True(blockActionRenders.All(r => r.Render)); @@ -1142,7 +1150,10 @@ public void GetStateOnlyDrillsDownUntilRequestedAddressesAreFound() Math.Min(10, addresses.Length - testingDepth - 1) ).Select(i => addresses[i]).ToArray(); - Assert.All(chain.GetStates(targetAddresses), Assert.NotNull); + Assert.All( + chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(targetAddresses), + Assert.NotNull); var callCount = tracker.Logs.Where( trackLog => trackLog.Method == "GetBlockStates" @@ -1175,7 +1186,8 @@ public void GetStateReturnsEarlyForNonexistentAccount() tracker.ClearLogs(); Address nonexistent = new PrivateKey().ToAddress(); - IValue result = chain.GetState(nonexistent); + IValue result = chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(nonexistent); Assert.Null(result); var callCount = tracker.Logs.Where( trackLog => trackLog.Method == "GetBlockStates" @@ -1198,7 +1210,8 @@ public void GetStateReturnsValidStateAfterFork() store, stateStore, new[] { new DumbAction(_fx.Address1, "item0.0", idempotent: true) }); - Assert.Equal("item0.0", (Text)chain.GetState(_fx.Address1)); + Assert.Equal("item0.0", (Text)chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(_fx.Address1)); chain.MakeTransaction( privateKey, @@ -1209,17 +1222,21 @@ public void GetStateReturnsValidStateAfterFork() chain.Append(block, CreateBlockCommit(block)); Assert.Equal( new IValue[] { (Text)"item0.0,item1.0" }, - chain.GetStates(new[] { _fx.Address1 }) + chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(new[] { _fx.Address1 }) ); - Assert.Equal("item0.0,item1.0", (Text)chain.GetState(_fx.Address1)); + Assert.Equal("item0.0,item1.0", (Text)chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(_fx.Address1)); var forked = chain.Fork(chain.Tip.Hash); Assert.Equal(2, forked.Count); Assert.Equal( new IValue[] { (Text)"item0.0,item1.0" }, - forked.GetStates(new[] { _fx.Address1 }) + forked.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(new[] { _fx.Address1 }) ); - Assert.Equal("item0.0,item1.0", (Text)forked.GetState(_fx.Address1)); + Assert.Equal("item0.0,item1.0", (Text)forked.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(_fx.Address1)); } [SkippableFact] @@ -1239,10 +1256,14 @@ public void GetStateReturnsLatestStatesWhenMultipleAddresses() _fx.StateStore, new SingleActionLoader(typeof(DumbAction)))); - Assert.All(chain.GetStates(addresses), Assert.Null); + Assert.All( + chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(addresses), + Assert.Null); foreach (var address in addresses) { - Assert.Null(chain.GetState(address)); + Assert.Null(chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address)); } var privateKeysAndAddresses10 = privateKeys.Zip(addresses, (k, a) => (k, a)); @@ -1256,19 +1277,25 @@ public void GetStateReturnsLatestStatesWhenMultipleAddresses() chain.Append(block1, CreateBlockCommit(block1)); - Assert.All(chain.GetStates(addresses), v => Assert.Equal((Text)"1", v)); + Assert.All( + chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(addresses), + v => Assert.Equal((Text)"1", v)); foreach (var address in addresses) { - Assert.Equal((Text)"1", chain.GetState(address)); + Assert.Equal((Text)"1", chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address)); } chain.MakeTransaction(privateKeys[0], new[] { new DumbAction(addresses[0], "2") }); Block block2 = chain.ProposeBlock( privateKeys[0], lastCommit: CreateBlockCommit(chain.Tip)); chain.Append(block2, CreateBlockCommit(block2)); - Assert.Equal((Text)"1,2", chain.GetState(addresses[0])); + Assert.Equal((Text)"1,2", chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(addresses[0])); Assert.All( - chain.GetStates(addresses.Skip(1).ToArray()), + chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(addresses.Skip(1).ToArray()), v => Assert.Equal((Text)"1", v) ); } @@ -1508,8 +1535,14 @@ public void MakeTransactionWithSystemAction() var action = new Initialize( new ValidatorSet( new List() { new Validator(new PrivateKey().PublicKey, 1) }), - new Dictionary - { [default] = (Text)"initial value" }.ToImmutableDictionary()); + new Dictionary> + { + [default] = + new Dictionary + { + [default] = (Text)"initial value", + }.ToImmutableDictionary(), + }.ToImmutableDictionary()); _blockChain.MakeTransaction(privateKey, actions: new IAction[] { action }); _blockChain.MakeTransaction(privateKey, actions: new IAction[] { action }); @@ -1605,9 +1638,12 @@ public void BlockActionWithMultipleAddress() miner2, lastCommit: CreateBlockCommit(_blockChain.Tip)); _blockChain.Append(block3, CreateBlockCommit(block3)); - IValue miner1state = _blockChain.GetState(miner1.ToAddress()); - IValue miner2state = _blockChain.GetState(miner2.ToAddress()); - IValue rewardState = _blockChain.GetState(rewardRecordAddress); + IValue miner1state = _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(miner1.ToAddress()); + IValue miner2state = _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(miner2.ToAddress()); + IValue rewardState = _blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(rewardRecordAddress); AssertBencodexEqual((Integer)2, miner1state); AssertBencodexEqual((Integer)1, miner2state); @@ -1695,7 +1731,7 @@ void BuildIndex(Guid id, Block block) // Build a store with incomplete states Block b = chain.Genesis; - IAccount previousState = actionEvaluator.PrepareInitialDelta(null); + IWorld previousState = actionEvaluator.PrepareInitialDelta(null); ActionEvaluation[] evals = actionEvaluator.EvaluateBlock(b, previousState).ToArray(); IImmutableDictionary dirty = evals.GetDirtyStates(); @@ -1852,7 +1888,8 @@ private void CreateWithGenesisBlock() var systemActions = new IAction[] { new Initialize( - states: ImmutableDictionary.Create(), + states: ImmutableDictionary.Create< + Address, IImmutableDictionary>(), validatorSet: new ValidatorSet( new List { @@ -1909,7 +1946,8 @@ private void CreateWithGenesisBlock() tx => !(tx.Actions is null) && tx.Actions.All(a => !Registry.IsSystemAction(a))).UpdatedAddresses); - var states = addresses.Select(address => blockChain.GetState(address)) + var states = addresses.Select(address => blockChain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(address)) .ToArray(); for (int i = 0; i < states.Length; ++i) { @@ -1991,8 +2029,10 @@ private void CheckIfTxPolicyExceptionHasInnerException() { // ReSharper disable AccessToModifiedClosure // The following method calls should not throw any exceptions: - x?.GetStates(new[] { default(Address) }); - x?.GetState(default); + x?.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetStates(new[] { default(Address) }); + x?.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(default); // ReSharper restore AccessToModifiedClosure }); IStore store = new MemoryStore(); @@ -2053,7 +2093,8 @@ private void ValidateNextBlockCommitOnValidatorSetChange() { new Initialize( validatorSet: initialValidatorSet, - states: ImmutableDictionary.Create() + states: ImmutableDictionary.Create< + Address, IImmutableDictionary>() ), }; var privateKey = new PrivateKey(); diff --git a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs index 1966eb0cf09..06dd9aa4770 100644 --- a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs @@ -16,7 +16,7 @@ public class AnonymousActionRendererTest { private static IValue _action = new DumbAction().PlainValue; - private static IAccount _account = new Account(MockAccountState.Empty); + private static IWorld _world = World.Create(new MockWorldState()); private static ICommittedActionContext _actionContext = new CommittedActionContext(new ActionContext( @@ -25,7 +25,7 @@ public class AnonymousActionRendererTest default, Block.CurrentProtocolVersion, default, - _account, + _world, default, 0)); @@ -55,11 +55,11 @@ record = (action, context, nextState), renderer.RenderBlock(_genesis, _blockA); Assert.Null(record); - renderer.RenderAction(_action, _actionContext, _account.Trie.Hash); + renderer.RenderAction(_action, _actionContext, _world.Trie.Hash); Assert.NotNull(record); Assert.Same(_action, record?.Item1); Assert.Same(_actionContext, record?.Item2); - Assert.Equal(_account.Trie.Hash, record?.Item3); + Assert.Equal(_world.Trie.Hash, record?.Item3); } [Fact] @@ -72,7 +72,7 @@ public void ActionErrorRenderer() record = (action, context, exception), }; - renderer.RenderAction(_action, _actionContext, _account.Trie.Hash); + renderer.RenderAction(_action, _actionContext, _world.Trie.Hash); Assert.Null(record); renderer.RenderBlock(_genesis, _blockA); Assert.Null(record); @@ -93,7 +93,7 @@ public void BlockRenderer() BlockRenderer = (oldTip, newTip) => record = (oldTip, newTip), }; - renderer.RenderAction(_action, _actionContext, _account.Trie.Hash); + renderer.RenderAction(_action, _actionContext, _world.Trie.Hash); Assert.Null(record); renderer.RenderActionError(_action, _actionContext, _exception); Assert.Null(record); diff --git a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs index 4c8220d8616..1af3c2c745a 100644 --- a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs @@ -23,7 +23,7 @@ public class LoggedActionRendererTest : IDisposable { private static IValue _action = new DumbAction().PlainValue; - private static IAccount _account = new Account(MockAccountState.Empty); + private static IWorld _world = World.Create(new MockWorldState()); private static Block _genesis = TestUtils.ProposeGenesisBlock(TestUtils.GenesisProposer); @@ -75,7 +75,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) default, Block.CurrentProtocolVersion, 123, - _account, + _world, default, 0, rehearsal)); @@ -112,7 +112,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) firstLog = logs[0]; Assert.Same(_action, action); Assert.Same(actionContext, cxt); - Assert.Equal(_account.Trie.Hash, next); + Assert.Equal(_world.Trie.Hash, next); called = true; if (exception) { @@ -147,7 +147,7 @@ public void ActionRenderings(bool error, bool rehearsal, bool exception) } else { - actionRenderer.RenderAction(_action, actionContext, _account.Trie.Hash); + actionRenderer.RenderAction(_action, actionContext, _world.Trie.Hash); } } catch (ThrowException.SomeException e) diff --git a/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs b/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs index ccde686aa6f..55316b4dd6b 100644 --- a/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs +++ b/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs @@ -2,6 +2,7 @@ using System.Security.Cryptography; using Libplanet.Action; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Action.Tests.Common; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -30,7 +31,8 @@ public PreEvaluationBlockTest(ITestOutputHelper output) public void Evaluate() { Address address = _contents.Block1Tx0.Signer; - var blockAction = new SetStatesAtBlock(address, (Bencodex.Types.Integer)123, 0); + var blockAction = new SetStatesAtBlock( + address, (Bencodex.Types.Integer)123, ReservedAddresses.LegacyAccount, 0); var policy = new BlockPolicy( blockAction: blockAction, blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000)); @@ -59,7 +61,8 @@ public void Evaluate() fx.StateStore, genesis, actionEvaluator); - AssertBencodexEqual((Bencodex.Types.Integer)123, blockChain.GetState(address)); + AssertBencodexEqual((Bencodex.Types.Integer)123, blockChain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(address)); HashDigest identicalGenesisStateRootHash = blockChain.DetermineBlockStateRootHash(preEvalGenesis, out _); @@ -86,7 +89,8 @@ public void Evaluate() AssertBytesEqual(block1.StateRootHash, identicalBlock1StateRootHash); blockChain.Append(block1, TestUtils.CreateBlockCommit(block1)); - AssertBencodexEqual((Bencodex.Types.Integer)158, blockChain.GetState(address)); + AssertBencodexEqual((Bencodex.Types.Integer)158, blockChain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(address)); } } @@ -94,7 +98,8 @@ public void Evaluate() public void DetermineStateRootHash() { Address address = _contents.Block1Tx0.Signer; - var blockAction = new SetStatesAtBlock(address, (Bencodex.Types.Integer)123, 0); + var blockAction = new SetStatesAtBlock( + address, (Bencodex.Types.Integer)123, ReservedAddresses.LegacyAccount, 0); var policy = new BlockPolicy( blockAction: blockAction, blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000)); @@ -123,7 +128,8 @@ public void DetermineStateRootHash() fx.StateStore, genesis, actionEvaluator); - AssertBencodexEqual((Bencodex.Types.Integer)123, blockChain.GetState(address)); + AssertBencodexEqual((Bencodex.Types.Integer)123, blockChain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(address)); HashDigest identicalGenesisStateRootHash = blockChain.DetermineBlockStateRootHash(preEvalGenesis, out _); @@ -148,7 +154,8 @@ public void DetermineStateRootHash() _output.WriteLine("#1: {0}", block1); blockChain.Append(block1, TestUtils.CreateBlockCommit(block1)); - AssertBencodexEqual((Bencodex.Types.Integer)158, blockChain.GetState(address)); + AssertBencodexEqual((Bencodex.Types.Integer)158, blockChain.GetWorldState() + .GetAccount(ReservedAddresses.LegacyAccount).GetState(address)); } } } diff --git a/Libplanet.Tests/Fixtures/Arithmetic.cs b/Libplanet.Tests/Fixtures/Arithmetic.cs index 1d6b47d1e91..fda0c7cc0f9 100644 --- a/Libplanet.Tests/Fixtures/Arithmetic.cs +++ b/Libplanet.Tests/Fixtures/Arithmetic.cs @@ -107,14 +107,15 @@ public void LoadPlainValue(IValue plainValue) Error = null; } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { if (Error != null) { throw new InvalidOperationException(Error); } - IValue v = context.PreviousState.GetState(context.Signer) ?? (Bencodex.Types.Integer)0; + IValue v = context.PreviousState.GetAccount(ReservedAddresses.LegacyAccount) + .GetState(context.Signer) ?? (Bencodex.Types.Integer)0; if (!(v is Bencodex.Types.Integer value)) { throw new InvalidOperationException( @@ -124,7 +125,11 @@ public IAccount Execute(IActionContext context) Func func = Operator.ToFunc(); BigInteger result = func(value, Operand); - return context.PreviousState.SetState(context.Signer, (Bencodex.Types.Integer)result); + return context.PreviousState.SetAccount( + ReservedAddresses.LegacyAccount, + context.PreviousState.GetAccount( + ReservedAddresses.LegacyAccount).SetState( + context.Signer, (Bencodex.Types.Integer)result)); } public bool Equals(Arithmetic other) diff --git a/Libplanet.Tests/Fixtures/BlockContentFixture.cs b/Libplanet.Tests/Fixtures/BlockContentFixture.cs index 2af59e76ca0..8cc1096bd54 100644 --- a/Libplanet.Tests/Fixtures/BlockContentFixture.cs +++ b/Libplanet.Tests/Fixtures/BlockContentFixture.cs @@ -48,7 +48,8 @@ public BlockContentFixture() { new Initialize( validatorSet: TestUtils.ValidatorSet, - states: ImmutableDictionary.Create()), + states: ImmutableDictionary.Create< + Address, IImmutableDictionary>()), }.ToPlainValues(), timestamp: DateTimeOffset.MinValue ); diff --git a/Libplanet.Tests/Fixtures/IntegerSet.cs b/Libplanet.Tests/Fixtures/IntegerSet.cs index 89bdc2592e1..03e3a2e8b10 100644 --- a/Libplanet.Tests/Fixtures/IntegerSet.cs +++ b/Libplanet.Tests/Fixtures/IntegerSet.cs @@ -112,9 +112,10 @@ public TxWithContext Sign(PrivateKey signer, params Arithmetic[] actions) long nonce = Chain.GetNextTxNonce(signerAddress); Transaction tx = Transaction.Create(nonce, signer, Genesis.Hash, actions.ToPlainValues()); - BigInteger prevState = Chain.GetState(signerAddress) is Bencodex.Types.Integer i - ? i.Value - : 0; + BigInteger prevState = Chain.GetWorldState().GetAccount( + ReservedAddresses.LegacyAccount).GetState(signerAddress) is Bencodex.Types.Integer i + ? i.Value + : 0; HashDigest prevStateRootHash = Chain.Tip.StateRootHash; ITrie prevTrie = GetTrie(Chain.Tip.Hash); (BigInteger, HashDigest) prevPair = (prevState, prevStateRootHash); @@ -178,11 +179,8 @@ public Block Propose() => Chain.ProposeBlock( public void Append(Block block) => Chain.Append(block, TestUtils.CreateBlockCommit(block)); - public IAccount CreateAccount(Address signer, BlockHash? offset = null) - => new Account(Chain.GetAccountState(offset)); - - public IAccount CreateAccount(int signerIndex, BlockHash? offset = null) - => CreateAccount(Addresses[signerIndex], offset); + public IWorld CreateWorld(BlockHash? offset = null) + => World.Create(Chain.GetWorldState(offset ?? Tip.Hash)); public ITrie GetTrie(BlockHash? blockHash) { diff --git a/Libplanet.Tests/Store/StoreTest.cs b/Libplanet.Tests/Store/StoreTest.cs index 09a3da08d9b..bbef746a61d 100644 --- a/Libplanet.Tests/Store/StoreTest.cs +++ b/Libplanet.Tests/Store/StoreTest.cs @@ -1266,7 +1266,7 @@ public void LoadPlainValue(Dictionary plainValue) Md5Digest = ((Binary)plainValue["md5"]).ByteArray; } - public IAccount Execute(IActionContext context) + public IWorld Execute(IActionContext context) { return context.PreviousState; } diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index 303f2e34921..fac9a5d7d49 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -427,7 +427,8 @@ public static PreEvaluationBlock ProposeGenesis( { new Initialize( validatorSet: validatorSet, - states: ImmutableDictionary.Create()), + states: ImmutableDictionary.Create< + Address, IImmutableDictionary>()), }.Select(x => x.PlainValue), timestamp: DateTimeOffset.MinValue))); txs = txs.OrderBy(tx => tx.Id).ToList(); diff --git a/Libplanet.Tests/Tx/TransactionTest.cs b/Libplanet.Tests/Tx/TransactionTest.cs index ef937792dd1..17131b724f9 100644 --- a/Libplanet.Tests/Tx/TransactionTest.cs +++ b/Libplanet.Tests/Tx/TransactionTest.cs @@ -4,6 +4,7 @@ using System.Linq; using Bencodex.Types; using Libplanet.Action; +using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Action.Tests.Common; using Libplanet.Common; @@ -71,8 +72,14 @@ public void CreateWithSystemAction() var action = new Initialize( new ValidatorSet(new List() { new Validator(privateKey.PublicKey, 1) }), - new Dictionary - { [default] = (Text)"initial value" }.ToImmutableDictionary()); + new Dictionary> + { + [ReservedAddresses.LegacyAccount] = + new Dictionary + { + [default] = (Text)"initial value", + }.ToImmutableDictionary(), + }.ToImmutableDictionary()); Transaction tx = Transaction.Create( 0, privateKey, @@ -88,18 +95,18 @@ public void CreateWithSystemAction() AssertBytesEqual( new byte[] { - 0x30, 0x44, 0x02, 0x20, 0x60, 0x8a, 0xbf, 0x3d, 0xdf, 0x50, - 0xf2, 0x00, 0x27, 0x56, 0xbb, 0x84, 0xc5, 0x65, 0x7c, 0xd9, - 0x4f, 0xbc, 0x33, 0x81, 0xe1, 0x0d, 0xc1, 0x5b, 0x23, 0x11, - 0xc4, 0x97, 0x61, 0x3e, 0xa0, 0xff, 0x02, 0x20, 0x44, 0x08, - 0xf8, 0x71, 0x43, 0x8c, 0x51, 0xd6, 0xa8, 0x22, 0x7d, 0x8c, - 0x7d, 0xb2, 0xc5, 0x97, 0x5b, 0xef, 0x72, 0x07, 0xfe, 0x4f, - 0x9d, 0x4c, 0x25, 0xc6, 0xb2, 0x1a, 0x89, 0xc7, 0xad, 0x10, + 0x30, 0x45, 0x02, 0x21, 0x00, 0xc8, 0x26, 0x79, 0x07, 0x90, + 0x38, 0x3d, 0x16, 0x7e, 0x2d, 0x44, 0x2a, 0x5e, 0xda, 0xbb, + 0xc3, 0xb2, 0xb1, 0x47, 0x7f, 0xe0, 0x69, 0x02, 0xb7, 0x39, + 0xd9, 0xed, 0x36, 0xb1, 0xd6, 0x8e, 0x5a, 0x02, 0x20, 0x4c, + 0x2a, 0x41, 0x12, 0x0b, 0x62, 0x96, 0x33, 0x67, 0xcb, 0xe2, + 0x84, 0x6d, 0xa4, 0xaf, 0x31, 0xf4, 0x96, 0xf8, 0x4c, 0xf2, + 0xc8, 0x62, 0x02, 0x3f, 0x20, 0x9a, 0x99, 0x52, 0x2b, 0x9c, 0x0d, }, tx.Signature ); AssertBytesEqual( - TxId.FromHex("eff61a6ee0faa2705b3dbd23abefbf18b4c864fa6b344c4fc3846cbb71a0e294"), + TxId.FromHex("34305c1d3e9f1026b64ae0021e2913b53130a87294d5adabd7b3e63a72ddcb6b"), tx.Id ); } diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index a96f12a461e..4515b1cdf51 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -515,7 +515,7 @@ public IReadOnlyList GetStates( GetAccountState(accountAddress, offset).GetStates(addresses); #pragma warning disable MEN002 - /// + /// #pragma warning restore MEN002 public IReadOnlyList GetStates( IReadOnlyList
addresses, @@ -566,6 +566,10 @@ public ValidatorSet GetValidatorSet(BlockHash? offset) => 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); /// From 1a03663f1c39c1e6a6f94c704ebb0bcac48261bf Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 20 Oct 2023 16:50:12 +0900 Subject: [PATCH 09/21] 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 . From fbcf1730a4889e3a20dd4aa5e1d4eb8e3b0b8d14 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 25 Oct 2023 18:00:47 +0900 Subject: [PATCH 10/21] feat: Fix BlockChain API from IAccountState --- Libplanet.Action/State/IBlockChainStates.cs | 89 +++++- .../GeneratedBlockChainFixture.cs | 4 +- .../Queries/StateQueryTest.cs | 63 ++-- Libplanet.Explorer/Queries/StateQuery.cs | 280 +++++++++++++----- Libplanet.Tests/Action/AccountTest.cs | 4 +- Libplanet.Tests/Action/AccountV0Test.cs | 2 +- Libplanet.Tests/Action/AccountV1Test.cs | 2 +- Libplanet/Blockchain/BlockChain.cs | 124 ++++++-- 8 files changed, 410 insertions(+), 158 deletions(-) diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index 716330dc652..09e1b257ce9 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -103,9 +103,7 @@ public interface IBlockChainStates /// 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 - /// . + /// An absent state is represented as . /// /// Thrown when is not /// and one of the following is true. @@ -119,10 +117,6 @@ 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); /// @@ -142,52 +136,115 @@ public interface IBlockChainStates /// /// The owner to query. /// The currency type to query. + /// The account to query from. /// The of the to fetch /// the states from. /// The 's balance for - /// at . If absent, returns 0 + /// at and . + /// If absent, returns 0 /// for . /// /// Thrown when at - /// cannot be created. + /// and cannot be created. + /// FungibleAssetValue GetBalance( Address address, Currency currency, + Address accountAddress, BlockHash? offset); + /// + /// Gets 's balance for given in the + /// at . + /// + /// The owner to query. + /// The currency type to query. + /// The state root hash of the to fetch + /// the balance from. + /// The 's balance for + /// at . + /// If absent, returns 0 for . + /// + /// Thrown when at + /// cannot be created. + FungibleAssetValue GetBalance( + Address address, + Currency currency, + HashDigest? stateRootHash); + /// /// Gets the total supply of a in the /// at , and if not found, returns 0. /// /// The currency type to query. + /// The account to query from. /// The of the to fetch /// the states from. /// The total supply value of at - /// in . + /// and + /// in . /// If absent, returns 0 /// for . /// Thrown when at - /// cannot be created. + /// and cannot be created. + /// /// Thrown when /// given 's /// is . - /// FungibleAssetValue GetTotalSupply( Currency currency, + Address accountAddress, BlockHash? offset); + /// + /// Gets the total supply of a in the + /// at , and if not found, returns 0. + /// + /// The currency type to query. + /// The state root hash of the to fetch + /// the total supply from. + /// The total supply value of at + /// in . + /// If absent, returns 0 + /// for . + /// Thrown when at + /// cannot be created. + /// Thrown when + /// given 's + /// is . + FungibleAssetValue GetTotalSupply( + Currency currency, + HashDigest? stateRootHash); + /// /// Returns the validator set in the /// at . /// + /// The account to query from. /// The of the to fetch /// the states from. /// The validator set of type at - /// . + /// and . + /// + /// Thrown when at + /// and cannot be created. + /// + ValidatorSet GetValidatorSet( + Address accountAddress, + BlockHash? offset); + + /// + /// Returns the validator set in the + /// at . + /// + /// The state root hash of the to fetch + /// the validator set from. + /// The validator set of type at + /// . /// /// Thrown when at - /// cannot be created. - /// - ValidatorSet GetValidatorSet(BlockHash? offset); + /// cannot be created. + ValidatorSet GetValidatorSet( + HashDigest? stateRootHash); } } diff --git a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs index 1f17ba483fe..a32ec751159 100644 --- a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs +++ b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs @@ -4,6 +4,7 @@ using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Loader; +using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; @@ -12,7 +13,6 @@ using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; using Libplanet.Types.Tx; -using Libplanet.Consensus; using Libplanet.Store; using Libplanet.Store.Trie; @@ -189,7 +189,7 @@ private Transaction { var random = new System.Random(seed); var addr = pk.ToAddress(); - var bal = (int)(Chain.GetBalance(addr, TestCurrency).MajorUnit & int.MaxValue); + var bal = (int)(Chain.GetBalance(addr, TestCurrency, ReservedAddresses.LegacyAccount).MajorUnit & int.MaxValue); return Transaction.Create( nonce, pk, diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index 818c94ad3ce..c98d14cc01e 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -117,6 +117,7 @@ public async Task Balance() balance( owner: ""0x5003712B63baAB98094aD678EA2B24BcE445D076"", currency: { ticker: ""ABC"", decimalPlaces: 2, totalSupplyTrackable: true }, + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetBlockHash: ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" ) { @@ -159,6 +160,7 @@ public async Task TotalSupply() { totalSupply( currency: { ticker: ""ABC"", decimalPlaces: 2, totalSupplyTrackable: true }, + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetBlockHash: ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" ) { @@ -212,6 +214,7 @@ public async Task Validators() ExecutionResult result = await ExecuteQueryAsync(@" { validators( + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetBlockHash: ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" ) { @@ -289,6 +292,7 @@ public async Task BalanceBySrh() balance( owner: ""0x5003712B63baAB98094aD678EA2B24BcE445D076"", currency: { ticker: ""ABC"", decimalPlaces: 2, totalSupplyTrackable: true }, + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetStateRootHash: ""c33b27773104f75ac9df5b0533854108bd498fab31e5236b6f1e1f6404d5ef64"" ) { @@ -331,6 +335,7 @@ public async Task TotalSupplyBySrh() { totalSupply( currency: { ticker: ""ABC"", decimalPlaces: 2, totalSupplyTrackable: true }, + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetStateRootHash: ""c33b27773104f75ac9df5b0533854108bd498fab31e5236b6f1e1f6404d5ef64"" ) { @@ -363,6 +368,7 @@ public async Task TotalSupplyBySrh() { totalSupply( currency: { ticker: ""LEG"", decimalPlaces: 0, totalSupplyTrackable: false }, + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetBlockHash: ""01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"" ) { @@ -384,6 +390,7 @@ public async Task ValidatorsBySrh() ExecutionResult result = await ExecuteQueryAsync(@" { validators( + accountAddress: ""0x1000000000000000000000000000000000000000"", offsetStateRootHash: ""c33b27773104f75ac9df5b0533854108bd498fab31e5236b6f1e1f6404d5ef64"" ) { @@ -406,50 +413,42 @@ public async Task ValidatorsBySrh() private class MockChainStates : IBlockChainStates { - public IReadOnlyList GetStates( - IReadOnlyList
addresses, - HashDigest? stateRootHash) => - addresses.Select(addr => GetAccountState(stateRootHash).GetState(addr)).ToList().AsReadOnly(); - - public FungibleAssetValue GetBalance( - Address address, Currency currency, BlockHash? offset) => - new MockAccount().GetBalance(address, currency); - - public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) => - new MockAccount().GetTotalSupply(currency); - - public ValidatorSet GetValidatorSet(BlockHash? offset) => - new MockAccount().GetValidatorSet(); - - - public IValue? GetState(Address address, Address accountAddress, BlockHash? offset) - => new MockAccount().GetState(address); + public IWorldState GetWorldState(HashDigest? hash) + => new MockWorld(); public IWorldState GetWorldState(BlockHash? offset) => new MockWorld(); + public IAccountState GetAccountState(HashDigest? hash) + => new MockAccount(); public IAccountState GetAccountState(Address address, BlockHash? blockHash) => new MockAccount(); - public ITrie GetBlockTrie(BlockHash? offset) - { - throw new System.NotImplementedException(); - } + public IValue? GetState(Address address, HashDigest? hash) + => new MockAccount().GetState(address); - public ITrie GetTrie(HashDigest? hash) - { - throw new System.NotImplementedException(); - } + public IValue? GetState(Address address, Address accountAddress, BlockHash? offset) + => new MockAccount().GetState(address); - public IWorldState GetWorldState(HashDigest? hash) - => new MockWorld(); + public FungibleAssetValue GetBalance( + Address address, Currency currency, HashDigest? hash) => + new MockAccount().GetBalance(address, currency); - public IAccountState GetAccountState(HashDigest? hash) - => new MockAccount(); + public FungibleAssetValue GetBalance( + Address address, Currency currency, Address accountAddress, BlockHash? offset) => + new MockAccount().GetBalance(address, currency); - public IValue? GetState(Address address, HashDigest? hash) - => new MockAccount().GetState(address); + public FungibleAssetValue GetTotalSupply(Currency currency, HashDigest? hash) => + new MockAccount().GetTotalSupply(currency); + + public FungibleAssetValue GetTotalSupply(Currency currency, Address accountAddress, BlockHash? offset) => + new MockAccount().GetTotalSupply(currency); + public ValidatorSet GetValidatorSet(HashDigest? hash) => + new MockAccount().GetValidatorSet(); + + public ValidatorSet GetValidatorSet(Address accountAddress, BlockHash? offset) => + new MockAccount().GetValidatorSet(); } private class MockWorld : IWorld diff --git a/Libplanet.Explorer/Queries/StateQuery.cs b/Libplanet.Explorer/Queries/StateQuery.cs index 6fd072b6e29..9197d51ddda 100644 --- a/Libplanet.Explorer/Queries/StateQuery.cs +++ b/Libplanet.Explorer/Queries/StateQuery.cs @@ -40,7 +40,7 @@ public StateQuery() "state", arguments: new QueryArguments( new QueryArgument> { Name = "address" }, - new QueryArgument> { Name = "accountAddress" }, + new QueryArgument { Name = "accountAddress" }, new QueryArgument { Name = "offsetBlockHash" }, new QueryArgument { Name = "offsetStateRootHash" }, new QueryArgument { Name = "accountStateRootHash" } @@ -52,8 +52,10 @@ public StateQuery() arguments: new QueryArguments( new QueryArgument> { Name = "owner" }, new QueryArgument> { Name = "currency" }, + new QueryArgument { Name = "accountAddress" }, new QueryArgument { Name = "offsetBlockHash" }, - new QueryArgument { Name = "offsetStateRootHash" } + new QueryArgument { Name = "offsetStateRootHash" }, + new QueryArgument { Name = "accountStateRootHash" } ), resolve: ResolveBalance ); @@ -61,16 +63,20 @@ public StateQuery() "totalSupply", arguments: new QueryArguments( new QueryArgument> { Name = "currency" }, + new QueryArgument { Name = "accountAddress" }, new QueryArgument { Name = "offsetBlockHash" }, - new QueryArgument { Name = "offsetStateRootHash" } + new QueryArgument { Name = "offsetStateRootHash" }, + new QueryArgument { Name = "accountStateRootHash" } ), resolve: ResolveTotalSupply ); Field>>( "validators", arguments: new QueryArguments( + new QueryArgument { Name = "accountAddress" }, new QueryArgument { Name = "offsetBlockHash" }, - new QueryArgument { Name = "offsetStateRootHash" } + new QueryArgument { Name = "offsetStateRootHash" }, + new QueryArgument { Name = "accountStateRootHash" } ), resolve: ResolveValidatorSet ); @@ -119,7 +125,7 @@ private static object ResolveWorldState( private static object ResolveAccountState( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { - Address? accountAddress = context.GetArgument
("accountAddress"); + Address? accountAddress = context.GetArgument("accountAddress"); string? offsetBlockHash = context.GetArgument("offsetBlockHash"); HashDigest? offsetStateRootHash = context .GetArgument?>("offsetStateRootHash"); @@ -193,7 +199,7 @@ private static object ResolveAccountState( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { Address address = context.GetArgument
("address"); - Address? accountAddress = context.GetArgument
("accountAddress"); + Address? accountAddress = context.GetArgument("accountAddress"); string? offsetBlockHash = context.GetArgument("offsetBlockHash"); HashDigest? offsetStateRootHash = context .GetArgument?>("offsetStateRootHash"); @@ -274,42 +280,79 @@ private static object ResolveBalance( { Address owner = context.GetArgument
("owner"); Currency currency = context.GetArgument("currency"); + 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) + .GetBalance(owner, currency); + } + 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.GetBalance(owner, currency, offset); - } + return context.Source.ChainStates + .GetWorldState(offset) + .GetAccount(accountAddr) + .GetBalance(owner, currency); + } - case (_, srh: not null): - return context.Source.ChainStates.GetWorldState(offsetStateRootHash) - .GetAccount(ReservedAddresses.LegacyAccount) - .GetBalance(owner, currency); + case (_, offsetSrh: not null): + return context.Source.ChainStates + .GetWorldState(offsetStateRootHash) + .GetAccount(accountAddr) + .GetBalance(owner, currency); + } + } + else + { + throw new ExecutionError( + "accountAddress have to be specified with offset." + ); + } } } @@ -317,9 +360,12 @@ private static object ResolveBalance( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { Currency currency = context.GetArgument("currency"); + Address? accountAddress = context.GetArgument("accountAddress"); string? offsetBlockHash = context.GetArgument("offsetBlockHash"); HashDigest? offsetStateRootHash = context .GetArgument?>("offsetStateRootHash"); + HashDigest? accountStateRootHash = context + .GetArgument?>("accountStateRootHash"); if (!currency.TotalSupplyTrackable) { @@ -330,81 +376,151 @@ private static object ResolveBalance( throw new ExecutionError(msg); } - 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) + .GetTotalSupply(currency); + } + 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.GetTotalSupply(currency, offset); - } + return context.Source.ChainStates + .GetWorldState(offset) + .GetAccount(accountAddr) + .GetTotalSupply(currency); + } - case (_, srh: not null): - return context.Source.ChainStates.GetWorldState(offsetStateRootHash) - .GetAccount(ReservedAddresses.LegacyAccount) - .GetTotalSupply(currency); + case (_, offsetSrh: not null): + return context.Source.ChainStates + .GetWorldState(offsetStateRootHash) + .GetAccount(accountAddr) + .GetTotalSupply(currency); + } + } + else + { + throw new ExecutionError( + "accountAddress have to be specified with offset." + ); + } } } private static object? ResolveValidatorSet( IResolveFieldContext<(IBlockChainStates ChainStates, IBlockPolicy Policy)> context) { + 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) + .GetValidatorSet().Validators; + } + 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.GetValidatorSet(offset).Validators; - } + return context.Source.ChainStates + .GetWorldState(offset) + .GetAccount(accountAddr) + .GetValidatorSet().Validators; + } - case (_, srh: not null): - return context.Source.ChainStates.GetWorldState(offsetStateRootHash) - .GetAccount(ReservedAddresses.LegacyAccount) - .GetValidatorSet() - .Validators; + case (_, offsetSrh: not null): + return context.Source.ChainStates + .GetWorldState(offsetStateRootHash) + .GetAccount(accountAddr) + .GetValidatorSet().Validators; + } + } + else + { + throw new ExecutionError( + "accountAddress have to be specified with offset." + ); + } } } } diff --git a/Libplanet.Tests/Action/AccountTest.cs b/Libplanet.Tests/Action/AccountTest.cs index a808e1d10bc..c79bc25b0d3 100644 --- a/Libplanet.Tests/Action/AccountTest.cs +++ b/Libplanet.Tests/Action/AccountTest.cs @@ -238,11 +238,11 @@ public virtual BlockChain TransferAssetInBlock() ); Assert.Equal( DumbAction.DumbCurrency * 5, - chain.GetBalance(_addr[0], DumbAction.DumbCurrency) + chain.GetBalance(_addr[0], DumbAction.DumbCurrency, ReservedAddresses.LegacyAccount) ); Assert.Equal( DumbAction.DumbCurrency * -5, - chain.GetBalance(_addr[1], DumbAction.DumbCurrency) + chain.GetBalance(_addr[1], DumbAction.DumbCurrency, ReservedAddresses.LegacyAccount) ); return chain; diff --git a/Libplanet.Tests/Action/AccountV0Test.cs b/Libplanet.Tests/Action/AccountV0Test.cs index d7f12211aee..02425711b55 100644 --- a/Libplanet.Tests/Action/AccountV0Test.cs +++ b/Libplanet.Tests/Action/AccountV0Test.cs @@ -81,7 +81,7 @@ public override BlockChain TransferAssetInBlock() chain.Append(block, TestUtils.CreateBlockCommit(block)); Assert.Equal( DumbAction.DumbCurrency * 6, - chain.GetBalance(_addr[0], DumbAction.DumbCurrency) + chain.GetBalance(_addr[0], DumbAction.DumbCurrency, ReservedAddresses.LegacyAccount) ); return chain; diff --git a/Libplanet.Tests/Action/AccountV1Test.cs b/Libplanet.Tests/Action/AccountV1Test.cs index 126e2ecc8ca..af2e6ac6b78 100644 --- a/Libplanet.Tests/Action/AccountV1Test.cs +++ b/Libplanet.Tests/Action/AccountV1Test.cs @@ -85,7 +85,7 @@ public override BlockChain TransferAssetInBlock() ); Assert.Equal( DumbAction.DumbCurrency * 5, - chain.GetBalance(_addr[0], DumbAction.DumbCurrency) + chain.GetBalance(_addr[0], DumbAction.DumbCurrency, ReservedAddresses.LegacyAccount) ); return chain; diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index b56b90a35a9..1d3d2172ecb 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -613,6 +613,10 @@ public IWorldState GetWorldState(BlockHash? offset) /// The current world state. public IWorldState GetWorldState() => GetWorldState(Tip.Hash); + /// + public IAccountState GetAccountState(HashDigest? stateRootHash) => + new AccountBaseState(GetTrie(stateRootHash)); + /// public IAccountState GetAccountState(Address address, BlockHash? offset) => GetWorldState(offset).GetAccount(address); @@ -627,18 +631,14 @@ public IAccountState GetAccountState(Address address, BlockHash? offset) => public IAccountState GetAccountState(Address address) => GetWorldState().GetAccount(address); - /// - public IAccountState GetAccountState(HashDigest? stateRootHash) => - new AccountBaseState(GetTrie(stateRootHash)); + /// + public IValue GetState(Address address, HashDigest? stateRootHash) => + GetAccountState(stateRootHash).GetState(address); /// 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 . @@ -652,47 +652,127 @@ public IValue GetState(Address address, HashDigest? stateRootHash) => public IValue GetState(Address address, Address accountAddress) => GetState(address, accountAddress, Tip.Hash); + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency, + HashDigest? stateRootHash) + => GetAccountState(stateRootHash) + .GetBalance(address, currency); + + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency, + Address accountAddress, + BlockHash? offset) + => GetWorldState(offset) + .GetAccount(accountAddress) + .GetBalance(address, currency); + /// /// Queries 's current balance of the /// in the . /// /// The owner to query. /// The currency type to query. + /// The account to query from. /// The 's current balance. /// + /// Thrown when of + /// cannot be created. + /// public FungibleAssetValue GetBalance( Address address, - Currency currency) - => GetBalance(address, currency, Tip.Hash); + Currency currency, + Address accountAddress) + => GetBalance(address, currency, accountAddress, Tip.Hash); - /// - public FungibleAssetValue GetBalance( - Address address, + /// + public FungibleAssetValue GetTotalSupply( + Currency currency, + HashDigest? stateRootHash) + => GetAccountState(stateRootHash) + .GetTotalSupply(currency); + + /// + public FungibleAssetValue GetTotalSupply( Currency currency, + Address accountAddress, BlockHash? offset) => GetWorldState(offset) - .GetAccount(ReservedAddresses.LegacyAccount).GetBalance(address, currency); + .GetAccount(accountAddress) + .GetTotalSupply(currency); /// /// Gets the current total supply of a in the /// . /// /// The currency type to query. + /// The account to query from. /// The total supply value of at - /// in . - public FungibleAssetValue GetTotalSupply(Currency currency) - => GetTotalSupply(currency, Tip.Hash); + /// and + /// in . + /// Thrown when of + /// cannot be created. + /// + public FungibleAssetValue GetTotalSupply(Currency currency, Address accountAddress) + => GetTotalSupply(currency, accountAddress, Tip.Hash); - /// - public FungibleAssetValue GetTotalSupply(Currency currency, BlockHash? offset) + /// + public ValidatorSet GetValidatorSet(HashDigest? stateRootHash) + => GetAccountState(stateRootHash) + .GetValidatorSet(); + + /// + public ValidatorSet GetValidatorSet(Address accountAddress, BlockHash? offset) => GetWorldState(offset) - .GetAccount(ReservedAddresses.LegacyAccount).GetTotalSupply(currency); + .GetAccount(accountAddress) + .GetValidatorSet(); - public ValidatorSet GetValidatorSet() => GetValidatorSet(Tip.Hash); + /// + /// Returns the current validator set in the . + /// + /// The account to query from. + /// The validator set of type at + /// and . + /// + /// Thrown when of + /// cannot be created. + /// + public ValidatorSet GetValidatorSet(Address accountAddress) + => GetValidatorSet(accountAddress, Tip.Hash); - /// + /// + /// Returns the validator set in the + /// at and + /// . + /// + /// The of the to fetch + /// the states from. + /// The validator set of type at + /// and . + /// + /// Thrown when at + /// and + /// cannot be created. + /// public ValidatorSet GetValidatorSet(BlockHash? offset) - => GetWorldState(offset).GetAccount(ReservedAddresses.LegacyAccount).GetValidatorSet(); + => GetValidatorSet(ReservedAddresses.LegacyAccount, offset); + + /// + /// Returns the current validator set in the . + /// + /// The validator set of type at + /// and . + /// + /// Thrown when of + /// cannot be created. + /// + public ValidatorSet GetValidatorSet() + => GetValidatorSet(ReservedAddresses.LegacyAccount); /// /// Queries the recorded for a successful or failed From dbed45ad8b7d16039273f084cd81001d8b3ac167 Mon Sep 17 00:00:00 2001 From: Chanhyuck Ko Date: Fri, 27 Oct 2023 19:00:14 +0900 Subject: [PATCH 11/21] docs: update CHANGES.md --- CHANGES.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ed4bd96927c..049127dc33e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,12 +10,105 @@ To be released. ### Backward-incompatible API changes + - Removed `BlockChainStates` class. [[#3462]] + - (Libplanet.Action) Changed `ActionEvaluator` to accept `IWorld` + instead of `IAccount`. [[#3462]] + - (Libplanet.Action) `IActionEvaluator.OutputState` became `IWorld`. + (was `IAccount`) [[#3462]] + - (Libplanet.Action) Added + `ActionEvaluationsExtensions.GetLegacyRawTotalDelta()` extension method. + [[#3462]] + - (Libplanet.Action) `ActionEvaluationsExtensions.GetRawTotalDelta()` + became to return + `IImmutableDictionary>`. + [[#3462]] + - (Libplanet.Action) `IAction.Execute()` became to return `IWorld`. + (was `IAccount`) [[#3462]] + - (Libplanet.Action) `IActionContext.PreviousState` became `IWorld`. + (was `IAccount`) [[#3462]] + - (Libplanet.Action) Following methods in `IFeeCollector` interface + became to accept and return `IWorld`. (was `IAccount`) [[#3462]] + - `IFeeCollector.Mortgage()` + - `IFeeCollector.Refund()` + - `IFeeCollector.Reward()` + - (Libplanet.Action) Renamed `AccountState` class to `AccountBaseState`. + [[#3462]] + - (Libplanet.Action) `IBlockChainStates` interface has modified. [[#3462]] + - Added `IBlockChainStates.GetWorldState()` method. + - Added `IBlockChainStates.GetAccountState(Address, BlockHash?)` method. + - Added `IBlockChainStates.GetState(Address, Address, BlockHash?)` method. + - Added `IBlockChainStates.GetState(Address, HashDigest?)` method. + - Added + `IBlockChainStates.GetBalance(Address, Currency, Address, BlockHash?)` + method. + - Added + `IBlockChainStates.GetBalance(Address, Currency, HashDigest?)` + method. + - Added + `IBlockChainStates.GetTotalSupply(Currency, Address, BlockHash?)` + method. + - Added + `IBlockChainStates.GetTotalSupply(Currency, HashDigest?)` + method. + - Added `IBlockChainStates.GetValidatorSet(Address, BlockHash?)` method. + - Added `IBlockChainStates.GetValidatorSet(HashDigest?)` method. + - Removed `IBlockChainStates.GetAccountState(BlockHash?)` method. + - Removed `IBlockChainStates.GetState(Address, BlockHash?)` method. + - Removed + `IBlockChainStates.GetStates(IReadOnlyList
, BlockHash?)` + method. + - Removed + `IBlockChainStates.GetBalance(Address, Currency, BlockHash?)` + method. + - Removed `IBlockChainStates.GetTotalSupply(Currency, BlockHash?)` method. + - Removed `IBlockChainStates.GetValidatorSet(BlockHash?)` method. + - (Libplanet.Action) `Initialize.Initialize()` method became to accept + `IImmutableDictionary>`. + (was `IImmutableDictionary`) [[#3462]] + - (Libplanet.Action) Type of `Initialize.States` property became + `IImmutableDictionary>?`. + (was `IImmutableDictionary?`) [[#3462]] + + ### Backward-incompatible network protocol changes ### Backward-incompatible storage format changes ### Added APIs + - (Libplanet.Action) Added `IWorld` interface and its implementation. + [[#3462]] + - Added `World` class. + - (Libplanet.Action) Added `IWorldDelta` interface. [[#3462]] + - (Libplanet.Action) Added `IWorldState` interface and its implementation. + [[#3462]] + - Added `WorldBaseState` class. + - (Libplanet.Action) Added `KeyConverters` static class. [[#3462]] + - (Libplanet.Action) Added `ReservedAddresses` static class. [[#3462]] + - (Libplanet.Action) Added `WorldDeltaExtensions` static class. [[#3462]] + - (Libplanet.Explorer) Added `AccountStateType` class. [[#3462]] + - (Libplanet.Explorer) Added `WorldStateType` class. [[#3462]] + - (Libplanet.Explorer) Added `WorldStateType` class. [[#3462]] + - (Libplanet.Explorer) Modified some fields of `StateQuery` query. [[#3462]] + - Added `StateQuery.worldState` field. + - Added `StateQuery.accountState` field. + - Added `accountAddress` of type `Address` argument + for `StateQuery.states` field. + - Added `accountStateRootHash` of type `HashDigest?` argument + for `StateQuery.states` field. + - Added `accountAddress` of type `Address` argument + for `StateQuery.balance` field. + - Added `accountStateRootHash` of type `HashDigest?` argument + for `StateQuery.balance` field. + - Added `accountAddress` of type `Address` argument + for `StateQuery.totalSupply` field. + - Added `accountStateRootHash` of type `HashDigest?` argument + for `StateQuery.totalSupply` field. + - Added `accountAddress` of type `Address` argument + for `StateQuery.validators` field. + - Added `accountStateRootHash` of type `HashDigest?` argument + for `StateQuery.validators` field. + ### Behavioral changes ### Bug fixes @@ -24,6 +117,8 @@ To be released. ### CLI tools +[#3462]: https://github.com/planetarium/libplanet/pull/3462 + Version 3.7.0 ------------- From e96ffb95c1263b0a81859f2fa81736d5bb10be4e Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 31 Oct 2023 17:21:43 +0900 Subject: [PATCH 12/21] Fix build warnings --- Libplanet.Action/ActionEvaluator.cs | 2 +- Libplanet.Action/State/World.cs | 49 +++++++++---------- .../Queries/StateQueryTest.cs | 4 +- Libplanet/Blockchain/BlockChain.cs | 14 +----- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 39123113482..86feb43f65c 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -671,7 +671,7 @@ internal IImmutableDictionary?> HashDigest? hash = null; if (iValue is IValue value) { - hash = new HashDigest(((Binary?)value)?.ToByteArray()); + hash = new HashDigest(value); } result.Add(key, hash); diff --git a/Libplanet.Action/State/World.cs b/Libplanet.Action/State/World.cs index cb46f789601..7d0d93faff1 100644 --- a/Libplanet.Action/State/World.cs +++ b/Libplanet.Action/State/World.cs @@ -38,29 +38,6 @@ public World(IWorldState baseState, IWorldDelta delta) [Pure] public bool Legacy { get; private set; } - /// - [Pure] - public IAccount GetAccount(Address address) - { - return Delta.Accounts.TryGetValue(address, out IAccount? account) - ? account! - : _baseState.GetAccount(address); - } - - /// - [Pure] - public IWorld SetAccount(Address address, IAccount account) - { - if (!address.Equals(ReservedAddresses.LegacyAccount) - && account.Delta.UpdatedFungibleAssets.Count > 0) - { - return this; - } - - return new World(this, new WorldDelta(Delta.Accounts.SetItem(address, account))) - { Legacy = Legacy && address.Equals(ReservedAddresses.LegacyAccount) }; - } - /// /// Creates a new World from given . /// @@ -69,8 +46,7 @@ public IWorld SetAccount(Address address, IAccount account) /// A null world using as its base state. /// public static IWorld Create(IWorldState previousWorld) => - new World(previousWorld) - { Legacy = previousWorld.Legacy }; + new World(previousWorld) { Legacy = previousWorld.Legacy }; /// /// Creates a null worlds from given . @@ -93,5 +69,28 @@ public static IWorld Flush(IWorld world) : throw new ArgumentException( $"Unknown type for {nameof(world)}: {world.GetType()}"); } + + /// + [Pure] + public IAccount GetAccount(Address address) + { + return Delta.Accounts.TryGetValue(address, out IAccount? account) + ? account + : _baseState.GetAccount(address); + } + + /// + [Pure] + public IWorld SetAccount(Address address, IAccount account) + { + if (!address.Equals(ReservedAddresses.LegacyAccount) + && account.Delta.UpdatedFungibleAssets.Count > 0) + { + return this; + } + + return new World(this, new WorldDelta(Delta.Accounts.SetItem(address, account))) + { Legacy = Legacy && address.Equals(ReservedAddresses.LegacyAccount) }; + } } } diff --git a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs index c98d14cc01e..1c164efb36c 100644 --- a/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/StateQueryTest.cs @@ -425,10 +425,10 @@ public IAccountState GetAccountState(HashDigest? hash) public IAccountState GetAccountState(Address address, BlockHash? blockHash) => new MockAccount(); - public IValue? GetState(Address address, HashDigest? hash) + public IValue GetState(Address address, HashDigest? hash) => new MockAccount().GetState(address); - public IValue? GetState(Address address, Address accountAddress, BlockHash? offset) + public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => new MockAccount().GetState(address); public FungibleAssetValue GetBalance( diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 1d3d2172ecb..09e2340549b 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -581,22 +581,12 @@ public IValue GetValue( /// /// retrieved from /// as sub state root hash. - /// Thrown if retrieved - /// cannot be cast as . public HashDigest GetSubStateRootHash( Address address, HashDigest? stateRootHash) { - try - { - IValue value = GetValue(address, stateRootHash); - return new HashDigest((Binary)value); - - throw new InvalidCastException($"IValue {value} cannot be cast as state root hash"); - } - finally - { - } + IValue value = GetValue(address, stateRootHash); + return new HashDigest(value); } /// From 898604358ffffeb76019a41b0ccd2a2fc558d521 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 31 Oct 2023 18:20:58 +0900 Subject: [PATCH 13/21] Reorganize and cleanup BlockChain --- Libplanet/Blockchain/BlockChain.States.cs | 190 ++++++++++++++++++ Libplanet/Blockchain/BlockChain.cs | 232 +--------------------- 2 files changed, 194 insertions(+), 228 deletions(-) create mode 100644 Libplanet/Blockchain/BlockChain.States.cs diff --git a/Libplanet/Blockchain/BlockChain.States.cs b/Libplanet/Blockchain/BlockChain.States.cs new file mode 100644 index 00000000000..0820505a7ee --- /dev/null +++ b/Libplanet/Blockchain/BlockChain.States.cs @@ -0,0 +1,190 @@ +#nullable disable +using System; +using System.Security.Cryptography; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Common; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; + +namespace Libplanet.Blockchain +{ + public partial class BlockChain + { + /// + 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(HashDigest? stateRootHash) => + new AccountBaseState(GetTrie(stateRootHash)); + + /// + 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 . + public IAccountState GetAccountState(Address address) => + GetWorldState().GetAccount(address); + + /// + public IValue GetState(Address address, HashDigest? stateRootHash) => + GetAccountState(stateRootHash).GetState(address); + + /// + public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => + GetAccountState(accountAddress, offset).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); + + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency, + HashDigest? stateRootHash) + => GetAccountState(stateRootHash) + .GetBalance(address, currency); + + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency, + Address accountAddress, + BlockHash? offset) + => GetWorldState(offset) + .GetAccount(accountAddress) + .GetBalance(address, currency); + + /// + /// Queries 's current balance of the + /// in the . + /// + /// The owner to query. + /// The currency type to query. + /// The account to query from. + /// The 's current balance. + /// + /// Thrown when of + /// cannot be created. + /// + public FungibleAssetValue GetBalance( + Address address, + Currency currency, + Address accountAddress) + => GetBalance(address, currency, accountAddress, Tip.Hash); + + /// + public FungibleAssetValue GetTotalSupply( + Currency currency, + HashDigest? stateRootHash) + => GetAccountState(stateRootHash) + .GetTotalSupply(currency); + + /// + public FungibleAssetValue GetTotalSupply( + Currency currency, + Address accountAddress, + BlockHash? offset) + => GetWorldState(offset) + .GetAccount(accountAddress) + .GetTotalSupply(currency); + + /// + /// Gets the current total supply of a in the + /// . + /// + /// The currency type to query. + /// The account to query from. + /// The total supply value of at + /// and + /// in . + /// Thrown when of + /// cannot be created. + /// + public FungibleAssetValue GetTotalSupply(Currency currency, Address accountAddress) + => GetTotalSupply(currency, accountAddress, Tip.Hash); + + /// + public ValidatorSet GetValidatorSet(HashDigest? stateRootHash) + => GetAccountState(stateRootHash) + .GetValidatorSet(); + + /// + public ValidatorSet GetValidatorSet(Address accountAddress, BlockHash? offset) + => GetWorldState(offset) + .GetAccount(accountAddress) + .GetValidatorSet(); + + /// + /// Returns the current validator set in the . + /// + /// The account to query from. + /// The validator set of type at + /// and . + /// + /// Thrown when of + /// cannot be created. + /// + public ValidatorSet GetValidatorSet(Address accountAddress) + => GetValidatorSet(accountAddress, Tip.Hash); + + /// + /// Returns the validator set in the + /// at and + /// . + /// + /// The of the to fetch + /// the states from. + /// The validator set of type at + /// and . + /// + /// Thrown when at + /// and + /// cannot be created. + /// + public ValidatorSet GetValidatorSet(BlockHash? offset) + => GetValidatorSet(ReservedAddresses.LegacyAccount, offset); + + /// + /// Returns the current validator set in the . + /// + /// The validator set of type at + /// and . + /// + /// Thrown when of + /// cannot be created. + /// + public ValidatorSet GetValidatorSet() + => GetValidatorSet(ReservedAddresses.LegacyAccount); + } +} diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 09e2340549b..f3612adb34c 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Security.Cryptography; using System.Threading; -using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Loader; using Libplanet.Action.State; @@ -20,14 +19,12 @@ using Libplanet.Store.Trie; using Libplanet.Types.Assets; using Libplanet.Types.Blocks; -using Libplanet.Types.Consensus; using Libplanet.Types.Tx; using Serilog; -using static Libplanet.Action.State.KeyConverters; namespace Libplanet.Blockchain { - #pragma warning disable MEN002 +#pragma warning disable MEN002 /// /// /// A class have s, s, and the chain @@ -42,7 +39,7 @@ namespace Libplanet.Blockchain /// /// This object is guaranteed that it has at least one block, since it takes a genesis /// block when it's instantiated. - #pragma warning restore MEN002 +#pragma warning restore MEN002 public partial class BlockChain : IBlockChainStates { // FIXME: The _rwlock field should be private. @@ -193,7 +190,7 @@ private BlockChain( // alternatives. internal event EventHandler<(Block OldTip, Block NewTip)> TipChanged; - #pragma warning disable MEN002 +#pragma warning disable MEN002 /// /// The list of registered renderers listening the state changes. /// @@ -203,7 +200,7 @@ private BlockChain( /// /// constructor instead. /// - #pragma warning restore MEN002 +#pragma warning restore MEN002 public IImmutableList Renderers { get; } /// @@ -543,227 +540,6 @@ public ITrie GetTrie(BlockHash? offset) } } - /// - /// Returns the from , with given - /// and . - /// - /// 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. - public HashDigest GetSubStateRootHash( - Address address, - HashDigest? stateRootHash) - { - IValue value = GetValue(address, stateRootHash); - return new HashDigest(value); - } - - /// - 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(HashDigest? stateRootHash) => - new AccountBaseState(GetTrie(stateRootHash)); - - /// - 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 IValue GetState(Address address, HashDigest? stateRootHash) => - GetAccountState(stateRootHash).GetState(address); - - /// - public IValue GetState(Address address, Address accountAddress, BlockHash? offset) => - GetAccountState(accountAddress, offset).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); - - /// - public FungibleAssetValue GetBalance( - Address address, - Currency currency, - HashDigest? stateRootHash) - => GetAccountState(stateRootHash) - .GetBalance(address, currency); - - /// - public FungibleAssetValue GetBalance( - Address address, - Currency currency, - Address accountAddress, - BlockHash? offset) - => GetWorldState(offset) - .GetAccount(accountAddress) - .GetBalance(address, currency); - - /// - /// Queries 's current balance of the - /// in the . - /// - /// The owner to query. - /// The currency type to query. - /// The account to query from. - /// The 's current balance. - /// - /// Thrown when of - /// cannot be created. - /// - public FungibleAssetValue GetBalance( - Address address, - Currency currency, - Address accountAddress) - => GetBalance(address, currency, accountAddress, Tip.Hash); - - /// - public FungibleAssetValue GetTotalSupply( - Currency currency, - HashDigest? stateRootHash) - => GetAccountState(stateRootHash) - .GetTotalSupply(currency); - - /// - public FungibleAssetValue GetTotalSupply( - Currency currency, - Address accountAddress, - BlockHash? offset) - => GetWorldState(offset) - .GetAccount(accountAddress) - .GetTotalSupply(currency); - - /// - /// Gets the current total supply of a in the - /// . - /// - /// The currency type to query. - /// The account to query from. - /// The total supply value of at - /// and - /// in . - /// Thrown when of - /// cannot be created. - /// - public FungibleAssetValue GetTotalSupply(Currency currency, Address accountAddress) - => GetTotalSupply(currency, accountAddress, Tip.Hash); - - /// - public ValidatorSet GetValidatorSet(HashDigest? stateRootHash) - => GetAccountState(stateRootHash) - .GetValidatorSet(); - - /// - public ValidatorSet GetValidatorSet(Address accountAddress, BlockHash? offset) - => GetWorldState(offset) - .GetAccount(accountAddress) - .GetValidatorSet(); - - /// - /// Returns the current validator set in the . - /// - /// The account to query from. - /// The validator set of type at - /// and . - /// - /// Thrown when of - /// cannot be created. - /// - public ValidatorSet GetValidatorSet(Address accountAddress) - => GetValidatorSet(accountAddress, Tip.Hash); - - /// - /// Returns the validator set in the - /// at and - /// . - /// - /// The of the to fetch - /// the states from. - /// The validator set of type at - /// and . - /// - /// Thrown when at - /// and - /// cannot be created. - /// - public ValidatorSet GetValidatorSet(BlockHash? offset) - => GetValidatorSet(ReservedAddresses.LegacyAccount, offset); - - /// - /// Returns the current validator set in the . - /// - /// The validator set of type at - /// and . - /// - /// Thrown when of - /// cannot be created. - /// - public ValidatorSet GetValidatorSet() - => GetValidatorSet(ReservedAddresses.LegacyAccount); - /// /// Queries the recorded for a successful or failed /// within a . From db63525a5daefe344a83e2747106e1b3028b2c37 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 1 Nov 2023 11:54:43 +0900 Subject: [PATCH 14/21] fix: Fix AccountMetrics to initialize --- Libplanet.Action/AccountMetrics.cs | 6 ++++++ Libplanet.Action/ActionEvaluator.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/Libplanet.Action/AccountMetrics.cs b/Libplanet.Action/AccountMetrics.cs index 94e5cd04381..e7810859320 100644 --- a/Libplanet.Action/AccountMetrics.cs +++ b/Libplanet.Action/AccountMetrics.cs @@ -7,5 +7,11 @@ public static class AccountMetrics { public static readonly AsyncLocal GetStateTimer = new AsyncLocal(); public static readonly AsyncLocal GetStateCount = new AsyncLocal(); + + public static void Initialize() + { + GetStateTimer.Value = new Stopwatch(); + GetStateCount.Value = 0; + } } } diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 86feb43f65c..798ee16524c 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -252,6 +252,7 @@ IActionContext CreateActionContext(IWorld newPrevState) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); + AccountMetrics.Initialize(); state = feeCollector.Mortgage(state); context = CreateActionContext(state); feeCollector = feeCollector.Next(context); From c9550a9cda416441b526045408c70680c104bab7 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 1 Nov 2023 11:57:09 +0900 Subject: [PATCH 15/21] Remove unused class --- .../ActionEvaluationsExtensions.cs | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 Libplanet.Action/ActionEvaluationsExtensions.cs diff --git a/Libplanet.Action/ActionEvaluationsExtensions.cs b/Libplanet.Action/ActionEvaluationsExtensions.cs deleted file mode 100644 index 730f241d182..00000000000 --- a/Libplanet.Action/ActionEvaluationsExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Libplanet.Action.State; -using Libplanet.Store.Trie; - -namespace Libplanet.Action -{ - public static class ActionEvaluationsExtensions - { - public static IImmutableDictionary> - GetRawTotalDelta( - this IReadOnlyList actionEvaluations) - { - return actionEvaluations - .Select(eval => eval.OutputState.Delta) - .ToList() - .OrderedSum() - .ToRawDelta(); - } - - public static IImmutableDictionary - GetLegacyRawTotalDelta( - this IEnumerable actionEvaluations) - { - return actionEvaluations - .SelectMany(eval => eval.OutputState.Delta.Accounts) - .Where(kv => kv.Key.Equals(ReservedAddresses.LegacyAccount)) - .Select(kv => kv.Value.Delta) - .ToList() - .OrderedSum() - .ToRawDelta(); - } - } -} From 22f36269cdc2b67488d140579d322cea03fb35ee Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 1 Nov 2023 14:23:10 +0900 Subject: [PATCH 16/21] Cleanup --- Libplanet.Action/ActionEvaluator.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Libplanet.Action/ActionEvaluator.cs b/Libplanet.Action/ActionEvaluator.cs index 798ee16524c..237a59d631b 100644 --- a/Libplanet.Action/ActionEvaluator.cs +++ b/Libplanet.Action/ActionEvaluator.cs @@ -621,7 +621,7 @@ internal IReadOnlyList } accountTrie = _stateStore.Commit(accountTrie); - worldTrie = worldTrie.Set(kv.Key, new Binary(accountTrie.Hash.ToByteArray())); + worldTrie = worldTrie.Set(kv.Key, new Binary(accountTrie.Hash.ByteArray)); } worldTrie = _stateStore.Commit(worldTrie); @@ -645,8 +645,7 @@ internal IReadOnlyList Stopwatch stopwatch = new Stopwatch(); worldTrie = _stateStore.GetStateRoot(null).Set( KeyConverters.ToStateKey(ReservedAddresses.LegacyAccount), - new Binary( - worldTrie.Hash.ToByteArray())); + new Binary(worldTrie.Hash.ByteArray)); _logger .ForContext("Tag", "Metric") @@ -668,12 +667,9 @@ internal IImmutableDictionary?> foreach (var updatedAddress in evaluation.OutputState.Delta.UpdatedAddresses) { var key = KeyConverters.ToStateKey(updatedAddress); - var iValue = worldTrie.Get(new KeyBytes[] { key }).First(); - HashDigest? hash = null; - if (iValue is IValue value) - { - hash = new HashDigest(value); - } + HashDigest? hash = worldTrie.Get(key) is { } value + ? new HashDigest(value) + : (HashDigest?)null; result.Add(key, hash); } From 66c611f974f61189e651a3f6dc71583a44ea1e85 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 2 Nov 2023 13:51:11 +0900 Subject: [PATCH 17/21] revert: Revert Keyconverters as internal --- CHANGES.md | 1 - Libplanet.Action/State/KeyConverters.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 049127dc33e..01b09e332a8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -83,7 +83,6 @@ To be released. - (Libplanet.Action) Added `IWorldState` interface and its implementation. [[#3462]] - Added `WorldBaseState` class. - - (Libplanet.Action) Added `KeyConverters` static class. [[#3462]] - (Libplanet.Action) Added `ReservedAddresses` static class. [[#3462]] - (Libplanet.Action) Added `WorldDeltaExtensions` static class. [[#3462]] - (Libplanet.Explorer) Added `AccountStateType` class. [[#3462]] diff --git a/Libplanet.Action/State/KeyConverters.cs b/Libplanet.Action/State/KeyConverters.cs index e40fd454b69..a43b0ae0681 100644 --- a/Libplanet.Action/State/KeyConverters.cs +++ b/Libplanet.Action/State/KeyConverters.cs @@ -4,7 +4,7 @@ namespace Libplanet.Action.State { - public static class KeyConverters + internal static class KeyConverters { // "___" public static readonly KeyBytes ValidatorSetKey = From 5a6dd333d163c074849e41d6a33355d7973f21ef Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 2 Nov 2023 13:51:58 +0900 Subject: [PATCH 18/21] feat: Remove World.Create() --- Libplanet.Action.Tests/ActionContextTest.cs | 10 +++++----- Libplanet.Action.Tests/ActionEvaluationTest.cs | 4 ++-- Libplanet.Action.Tests/Sys/InitializeTest.cs | 4 ++-- Libplanet.Action/State/World.cs | 12 +----------- Libplanet.Tests/Action/AccountV0Test.cs | 2 +- Libplanet.Tests/Action/AccountV1Test.cs | 2 +- .../Renderers/AnonymousActionRendererTest.cs | 2 +- .../Blockchain/Renderers/LoggedActionRendererTest.cs | 2 +- Libplanet.Tests/Fixtures/IntegerSet.cs | 2 +- 9 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Libplanet.Action.Tests/ActionContextTest.cs b/Libplanet.Action.Tests/ActionContextTest.cs index 58344f4fe63..555fcd3e0da 100644 --- a/Libplanet.Action.Tests/ActionContextTest.cs +++ b/Libplanet.Action.Tests/ActionContextTest.cs @@ -36,7 +36,7 @@ public void RandomShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: World.Create(new MockWorldState()), + previousState: new World(new MockWorldState()), randomSeed: seed, gasLimit: 0 ); @@ -54,7 +54,7 @@ public void GuidShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: World.Create(new MockWorldState()), + previousState: new World(new MockWorldState()), randomSeed: 0, gasLimit: 0 ); @@ -65,7 +65,7 @@ public void GuidShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: World.Create(new MockWorldState()), + previousState: new World(new MockWorldState()), randomSeed: 0, gasLimit: 0 ); @@ -76,7 +76,7 @@ public void GuidShouldBeDeterministic() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: World.Create(new MockWorldState()), + previousState: new World(new MockWorldState()), randomSeed: 1, gasLimit: 0 ); @@ -115,7 +115,7 @@ public void GuidVersionAndVariant() miner: _address, blockIndex: 1, blockProtocolVersion: Block.CurrentProtocolVersion, - previousState: World.Create(new MockWorldState()), + previousState: new World(new MockWorldState()), randomSeed: i, gasLimit: 0 ); diff --git a/Libplanet.Action.Tests/ActionEvaluationTest.cs b/Libplanet.Action.Tests/ActionEvaluationTest.cs index 663b2740a5f..fe799e5030d 100644 --- a/Libplanet.Action.Tests/ActionEvaluationTest.cs +++ b/Libplanet.Action.Tests/ActionEvaluationTest.cs @@ -37,11 +37,11 @@ public void Constructor() address, 1, Block.CurrentProtocolVersion, - World.Create(new MockWorldState()), + new World(new MockWorldState()), 123, 0, false), - World.Create( + new World( new MockWorldState().SetAccount( ReservedAddresses.LegacyAccount, new Account(MockAccountState.Empty.SetState(address, (Text)"item")))) diff --git a/Libplanet.Action.Tests/Sys/InitializeTest.cs b/Libplanet.Action.Tests/Sys/InitializeTest.cs index 4bba35ae0ed..bc9f302e936 100644 --- a/Libplanet.Action.Tests/Sys/InitializeTest.cs +++ b/Libplanet.Action.Tests/Sys/InitializeTest.cs @@ -47,7 +47,7 @@ public void Execute() { var random = new System.Random(); Address signer = random.NextAddress(); - var prevState = World.Create(new MockWorldState()); + var prevState = new World(new MockWorldState()); BlockHash genesisHash = random.NextBlockHash(); var context = new ActionContext( signer: signer, @@ -79,7 +79,7 @@ public void ExecuteInNonGenesis() { var random = new System.Random(); Address signer = random.NextAddress(); - var prevState = World.Create(new MockWorldState()); + var prevState = new World(new MockWorldState()); BlockHash genesisHash = random.NextBlockHash(); var context = new ActionContext( signer: signer, diff --git a/Libplanet.Action/State/World.cs b/Libplanet.Action/State/World.cs index 7d0d93faff1..220a6938887 100644 --- a/Libplanet.Action/State/World.cs +++ b/Libplanet.Action/State/World.cs @@ -23,7 +23,7 @@ public World(IWorldState baseState, IWorldDelta delta) { _baseState = baseState; Delta = delta; - Legacy = true; + Legacy = baseState.Legacy; } /// @@ -38,16 +38,6 @@ public World(IWorldState baseState, IWorldDelta delta) [Pure] public bool Legacy { get; private set; } - /// - /// Creates a new World from given . - /// - /// The previous to initialize - /// new World. - /// A null world using as its base state. - /// - public static IWorld Create(IWorldState previousWorld) => - new World(previousWorld) { Legacy = previousWorld.Legacy }; - /// /// Creates a null worlds from given . /// diff --git a/Libplanet.Tests/Action/AccountV0Test.cs b/Libplanet.Tests/Action/AccountV0Test.cs index 02425711b55..b2de5edc7fc 100644 --- a/Libplanet.Tests/Action/AccountV0Test.cs +++ b/Libplanet.Tests/Action/AccountV0Test.cs @@ -26,7 +26,7 @@ public AccountV0Test(ITestOutputHelper output) public override IActionContext CreateContext( IAccount delta, Address signer) { - IWorld world = World.Create(new MockWorldState()); + IWorld world = new World(new MockWorldState()); world = world.SetAccount(_accountAddress, delta); return new ActionContext( signer, diff --git a/Libplanet.Tests/Action/AccountV1Test.cs b/Libplanet.Tests/Action/AccountV1Test.cs index af2e6ac6b78..25c31e782d1 100644 --- a/Libplanet.Tests/Action/AccountV1Test.cs +++ b/Libplanet.Tests/Action/AccountV1Test.cs @@ -28,7 +28,7 @@ public AccountV1Test(ITestOutputHelper output) public override IActionContext CreateContext(IAccount delta, Address signer) { - IWorld world = World.Create(new MockWorldState()); + IWorld world = new World(new MockWorldState()); world = world.SetAccount(_accountAddress, delta); return new ActionContext( signer, diff --git a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs index 06dd9aa4770..12a7b9883c8 100644 --- a/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/AnonymousActionRendererTest.cs @@ -16,7 +16,7 @@ public class AnonymousActionRendererTest { private static IValue _action = new DumbAction().PlainValue; - private static IWorld _world = World.Create(new MockWorldState()); + private static IWorld _world = new World(new MockWorldState()); private static ICommittedActionContext _actionContext = new CommittedActionContext(new ActionContext( diff --git a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs index 1af3c2c745a..c2893af9b56 100644 --- a/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs +++ b/Libplanet.Tests/Blockchain/Renderers/LoggedActionRendererTest.cs @@ -23,7 +23,7 @@ public class LoggedActionRendererTest : IDisposable { private static IValue _action = new DumbAction().PlainValue; - private static IWorld _world = World.Create(new MockWorldState()); + private static IWorld _world = new World(new MockWorldState()); private static Block _genesis = TestUtils.ProposeGenesisBlock(TestUtils.GenesisProposer); diff --git a/Libplanet.Tests/Fixtures/IntegerSet.cs b/Libplanet.Tests/Fixtures/IntegerSet.cs index 03e3a2e8b10..99966d63b4b 100644 --- a/Libplanet.Tests/Fixtures/IntegerSet.cs +++ b/Libplanet.Tests/Fixtures/IntegerSet.cs @@ -180,7 +180,7 @@ public void Append(Block block) => Chain.Append(block, TestUtils.CreateBlockCommit(block)); public IWorld CreateWorld(BlockHash? offset = null) - => World.Create(Chain.GetWorldState(offset ?? Tip.Hash)); + => new World(Chain.GetWorldState(offset ?? Tip.Hash)); public ITrie GetTrie(BlockHash? blockHash) { From 0a225e04d14a9eb2762476937e9ef90c293b3f76 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Tue, 7 Nov 2023 13:46:20 +0900 Subject: [PATCH 19/21] Apply minor changes from reviews --- Libplanet.Action/State/WorldBaseState.cs | 6 ++---- Libplanet.Tests/Blockchain/BlockChainTest.Append.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Libplanet.Action/State/WorldBaseState.cs b/Libplanet.Action/State/WorldBaseState.cs index 5f39a49f109..e36506e905e 100644 --- a/Libplanet.Action/State/WorldBaseState.cs +++ b/Libplanet.Action/State/WorldBaseState.cs @@ -47,11 +47,9 @@ public WorldBaseState(ITrie trie, IStateStore stateStore) /// The list of s of the given /// . public IReadOnlyList GetAccounts(IReadOnlyList
addresses) => - GetAccountStateRoot(addresses) - .Zip(addresses, (trie, address) => CreateAccount(trie)) - .ToList(); + GetAccountStateRoots(addresses).Select(CreateAccount).ToList(); - private IReadOnlyList GetAccountStateRoot(IReadOnlyList
addresses) + private IReadOnlyList GetAccountStateRoots(IReadOnlyList
addresses) { if (Legacy) { diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index 160ca8109c0..640db5a7e5c 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -298,21 +298,21 @@ public void AppendModern() TestUtils.CreateBlockCommit(_blockChain.Tip)); var commit1 = TestUtils.CreateBlockCommit(block1); _blockChain.Append(block1, commit1); + var world1 = _blockChain.GetWorldState(); + Assert.False(world1.Legacy); Assert.Equal( (Text)"foo", - _blockChain.GetWorldState() - .GetAccount(DumbModernAction.DumbModernAddress) - .GetState(address1)); + world1.GetAccount(DumbModernAction.DumbModernAddress).GetState(address1)); var block2 = _blockChain.ProposeBlock( miner, new[] { tx2 }.ToImmutableList(), commit1); _blockChain.Append(block2, TestUtils.CreateBlockCommit(block2)); + var world2 = _blockChain.GetWorldState(); + Assert.False(world2.Legacy); Assert.Equal( (Text)"bar", - _blockChain.GetWorldState() - .GetAccount(DumbModernAction.DumbModernAddress) - .GetState(address2)); + world2.GetAccount(DumbModernAction.DumbModernAddress).GetState(address2)); } [SkippableFact] From 78011b62a579d02525bc5b5b2006f2cc8c99a681 Mon Sep 17 00:00:00 2001 From: ilgyu Date: Wed, 8 Nov 2023 13:28:29 +0900 Subject: [PATCH 20/21] revert: Revert Initialize action --- Libplanet.Action.Tests/Sys/InitializeTest.cs | 12 ++---- Libplanet.Action.Tests/Sys/RegistryTest.cs | 10 ++--- Libplanet.Action/Sys/Initialize.cs | 39 ++++++------------- .../GeneratedBlockChainFixture.cs | 3 +- .../Queries/TransactionQueryTest.cs | 8 +--- .../Commands/BlockCommand.cs | 2 +- Libplanet.Tests/Blockchain/BlockChainTest.cs | 17 +++----- .../Fixtures/BlockContentFixture.cs | 3 +- Libplanet.Tests/TestUtils.cs | 3 +- Libplanet.Tests/Tx/TransactionTest.cs | 25 +++++------- 10 files changed, 39 insertions(+), 83 deletions(-) diff --git a/Libplanet.Action.Tests/Sys/InitializeTest.cs b/Libplanet.Action.Tests/Sys/InitializeTest.cs index bc9f302e936..51b2287aeb4 100644 --- a/Libplanet.Action.Tests/Sys/InitializeTest.cs +++ b/Libplanet.Action.Tests/Sys/InitializeTest.cs @@ -20,15 +20,11 @@ public class InitializeTest } ); - private static readonly IImmutableDictionary> + private static readonly IImmutableDictionary _states = - new Dictionary> + new Dictionary { - [default] = - new Dictionary - { - [default] = (Text)"initial value", - }.ToImmutableDictionary(), + [default] = (Text)"initial value", }.ToImmutableDictionary(); [Fact] @@ -70,7 +66,7 @@ public void Execute() _validatorSet, nextState.GetAccount(ReservedAddresses.LegacyAccount).GetValidatorSet()); Assert.Equal( - _states[default][default], + _states[default], nextState.GetAccount(default).GetState(default)); } diff --git a/Libplanet.Action.Tests/Sys/RegistryTest.cs b/Libplanet.Action.Tests/Sys/RegistryTest.cs index 489372d5461..f72a6deef8d 100644 --- a/Libplanet.Action.Tests/Sys/RegistryTest.cs +++ b/Libplanet.Action.Tests/Sys/RegistryTest.cs @@ -19,15 +19,11 @@ public class RegistryTest } ); - private static readonly IImmutableDictionary> + private static readonly IImmutableDictionary _states = - new Dictionary> + new Dictionary { - [default] = - new Dictionary - { - [default] = (Text)"initial value", - }.ToImmutableDictionary(), + [default] = (Text)"initial value", }.ToImmutableDictionary(); private static readonly Currency FooCurrency = Currency.Uncapped("FOO", 2, null); diff --git a/Libplanet.Action/Sys/Initialize.cs b/Libplanet.Action/Sys/Initialize.cs index f2021a165e9..a249fd9240f 100644 --- a/Libplanet.Action/Sys/Initialize.cs +++ b/Libplanet.Action/Sys/Initialize.cs @@ -18,7 +18,7 @@ public sealed class Initialize : IAction { public Initialize( ValidatorSet validatorSet, - IImmutableDictionary> states + IImmutableDictionary states ) { ValidatorSet = validatorSet; @@ -30,7 +30,7 @@ public Initialize() // Used only for deserialization. See also class Libplanet.Action.Sys.Registry. } - public IImmutableDictionary>? States + public IImmutableDictionary? States { get; private set; @@ -51,12 +51,7 @@ public IValue PlainValue s.Select( kv => new KeyValuePair( (Binary)kv.Key.Bencoded, - new Dictionary(kv.Value.Select( - kv2 => new KeyValuePair( - (Binary)kv2.Key.Bencoded, - kv2.Value - ) - )) + kv.Value ) ) ) @@ -71,6 +66,7 @@ public IValue PlainValue public IWorld Execute(IActionContext context) { IWorld world = context.PreviousState; + IAccount legacyAccount = world.GetAccount(ReservedAddresses.LegacyAccount); if (context.BlockIndex != 0) { @@ -81,30 +77,22 @@ public IWorld Execute(IActionContext context) if (ValidatorSet is { } vs) { - IAccount account = world.GetValidatorSetAccount(); - foreach (Validator v in vs.Validators) { - account = account.SetValidator(v); + legacyAccount = legacyAccount.SetValidator(v); } - - world = world.SetAccount(ReservedAddresses.LegacyAccount, account); } if (States is { } s) { - foreach (KeyValuePair> kv in s) + foreach (KeyValuePair kv in s) { - var acc = world.GetAccount(kv.Key); - foreach (KeyValuePair kv2 in kv.Value) - { - acc = acc.SetState(kv2.Key, kv2.Value); - } - - world = world.SetAccount(kv.Key, acc); + legacyAccount = legacyAccount.SetState(kv.Key, kv.Value); } } + world = world.SetAccount(ReservedAddresses.LegacyAccount, legacyAccount); + return world; } @@ -148,13 +136,8 @@ public void LoadPlainValue(IValue plainValue) ValidatorSet = new ValidatorSet((List)valuesList[0]); States = ((Dictionary)valuesList[1]) - .Select(kv => new KeyValuePair>( - new Address(kv.Key), - ((Dictionary)kv.Value) - .Select(kv2 => new KeyValuePair( - new Address(kv2.Key), - kv2.Value)) - .ToImmutableDictionary())) + .Select(kv => new KeyValuePair( + new Address(kv.Key), kv.Value)) .ToImmutableDictionary(); } } diff --git a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs index a32ec751159..734db62b40b 100644 --- a/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs +++ b/Libplanet.Explorer.Tests/GeneratedBlockChainFixture.cs @@ -102,8 +102,7 @@ public GeneratedBlockChainFixture( new ValidatorSet( ImmutableList.Empty.Add( new Validator(pk.PublicKey, 1)).ToList()), - ImmutableDictionary.Create< - Address, IImmutableDictionary>()) + ImmutableDictionary.Create()) }.ToPlainValues())) .ToImmutableList()); Chain = BlockChain.Create( diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs index 7eff5eba2d6..13e4dd48d47 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs @@ -75,13 +75,9 @@ public async Task BindSignatureWithSystemAction() var action = new Initialize( new ValidatorSet(new List() { new Validator(new PrivateKey().PublicKey, 1 )}), - new Dictionary> + new Dictionary { - [default] = - new Dictionary - { - [default] = (Text)"initial value", - }.ToImmutableDictionary(), + [default] = (Text)"initial value" }.ToImmutableDictionary()); var tx = Transaction.Create( 0L, diff --git a/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs b/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs index e5975a47632..55f34fdb16b 100644 --- a/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs +++ b/Libplanet.Extensions.Cocona/Commands/BlockCommand.cs @@ -139,7 +139,7 @@ public void GenerateGenesis( .Select(k => new Validator(k, BigInteger.One)) .ToList()); var emptyState = - ImmutableTrieDictionary>.Empty; + ImmutableTrieDictionary.Empty; ImmutableList txs = Array.Empty() // FIXME: Remove this pragma after fixing the following issue: diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index 10c1e05e0b7..81c166f21a2 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -193,8 +193,7 @@ public void ProcessActions() { new Initialize( validatorSet: TestUtils.ValidatorSet, - states: ImmutableDictionary.Create - >()), + states: ImmutableDictionary.Create()), }.ToPlainValues(), timestamp: DateTimeOffset.UtcNow)) .OrderBy(tx => tx.Id) @@ -1535,13 +1534,9 @@ public void MakeTransactionWithSystemAction() var action = new Initialize( new ValidatorSet( new List() { new Validator(new PrivateKey().PublicKey, 1) }), - new Dictionary> + new Dictionary { - [default] = - new Dictionary - { - [default] = (Text)"initial value", - }.ToImmutableDictionary(), + [default] = (Text)"initial value", }.ToImmutableDictionary()); _blockChain.MakeTransaction(privateKey, actions: new IAction[] { action }); @@ -1888,8 +1883,7 @@ private void CreateWithGenesisBlock() var systemActions = new IAction[] { new Initialize( - states: ImmutableDictionary.Create< - Address, IImmutableDictionary>(), + states: ImmutableDictionary.Create(), validatorSet: new ValidatorSet( new List { @@ -2093,8 +2087,7 @@ private void ValidateNextBlockCommitOnValidatorSetChange() { new Initialize( validatorSet: initialValidatorSet, - states: ImmutableDictionary.Create< - Address, IImmutableDictionary>() + states: ImmutableDictionary.Create() ), }; var privateKey = new PrivateKey(); diff --git a/Libplanet.Tests/Fixtures/BlockContentFixture.cs b/Libplanet.Tests/Fixtures/BlockContentFixture.cs index 8cc1096bd54..2af59e76ca0 100644 --- a/Libplanet.Tests/Fixtures/BlockContentFixture.cs +++ b/Libplanet.Tests/Fixtures/BlockContentFixture.cs @@ -48,8 +48,7 @@ public BlockContentFixture() { new Initialize( validatorSet: TestUtils.ValidatorSet, - states: ImmutableDictionary.Create< - Address, IImmutableDictionary>()), + states: ImmutableDictionary.Create()), }.ToPlainValues(), timestamp: DateTimeOffset.MinValue ); diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index fac9a5d7d49..303f2e34921 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -427,8 +427,7 @@ public static PreEvaluationBlock ProposeGenesis( { new Initialize( validatorSet: validatorSet, - states: ImmutableDictionary.Create< - Address, IImmutableDictionary>()), + states: ImmutableDictionary.Create()), }.Select(x => x.PlainValue), timestamp: DateTimeOffset.MinValue))); txs = txs.OrderBy(tx => tx.Id).ToList(); diff --git a/Libplanet.Tests/Tx/TransactionTest.cs b/Libplanet.Tests/Tx/TransactionTest.cs index 17131b724f9..301f7fbd666 100644 --- a/Libplanet.Tests/Tx/TransactionTest.cs +++ b/Libplanet.Tests/Tx/TransactionTest.cs @@ -4,7 +4,6 @@ using System.Linq; using Bencodex.Types; using Libplanet.Action; -using Libplanet.Action.State; using Libplanet.Action.Sys; using Libplanet.Action.Tests.Common; using Libplanet.Common; @@ -72,13 +71,9 @@ public void CreateWithSystemAction() var action = new Initialize( new ValidatorSet(new List() { new Validator(privateKey.PublicKey, 1) }), - new Dictionary> + new Dictionary { - [ReservedAddresses.LegacyAccount] = - new Dictionary - { - [default] = (Text)"initial value", - }.ToImmutableDictionary(), + [default] = (Text)"initial value", }.ToImmutableDictionary()); Transaction tx = Transaction.Create( 0, @@ -95,18 +90,18 @@ public void CreateWithSystemAction() AssertBytesEqual( new byte[] { - 0x30, 0x45, 0x02, 0x21, 0x00, 0xc8, 0x26, 0x79, 0x07, 0x90, - 0x38, 0x3d, 0x16, 0x7e, 0x2d, 0x44, 0x2a, 0x5e, 0xda, 0xbb, - 0xc3, 0xb2, 0xb1, 0x47, 0x7f, 0xe0, 0x69, 0x02, 0xb7, 0x39, - 0xd9, 0xed, 0x36, 0xb1, 0xd6, 0x8e, 0x5a, 0x02, 0x20, 0x4c, - 0x2a, 0x41, 0x12, 0x0b, 0x62, 0x96, 0x33, 0x67, 0xcb, 0xe2, - 0x84, 0x6d, 0xa4, 0xaf, 0x31, 0xf4, 0x96, 0xf8, 0x4c, 0xf2, - 0xc8, 0x62, 0x02, 0x3f, 0x20, 0x9a, 0x99, 0x52, 0x2b, 0x9c, 0x0d, + 0x30, 0x44, 0x02, 0x20, 0x60, 0x8a, 0xbf, 0x3d, 0xdf, 0x50, + 0xf2, 0x00, 0x27, 0x56, 0xbb, 0x84, 0xc5, 0x65, 0x7c, 0xd9, + 0x4f, 0xbc, 0x33, 0x81, 0xe1, 0x0d, 0xc1, 0x5b, 0x23, 0x11, + 0xc4, 0x97, 0x61, 0x3e, 0xa0, 0xff, 0x02, 0x20, 0x44, 0x08, + 0xf8, 0x71, 0x43, 0x8c, 0x51, 0xd6, 0xa8, 0x22, 0x7d, 0x8c, + 0x7d, 0xb2, 0xc5, 0x97, 0x5b, 0xef, 0x72, 0x07, 0xfe, 0x4f, + 0x9d, 0x4c, 0x25, 0xc6, 0xb2, 0x1a, 0x89, 0xc7, 0xad, 0x10, }, tx.Signature ); AssertBytesEqual( - TxId.FromHex("34305c1d3e9f1026b64ae0021e2913b53130a87294d5adabd7b3e63a72ddcb6b"), + TxId.FromHex("eff61a6ee0faa2705b3dbd23abefbf18b4c864fa6b344c4fc3846cbb71a0e294"), tx.Id ); } From 65ba613e5fc2937db0193538231048050a3f5eef Mon Sep 17 00:00:00 2001 From: ilgyu Date: Thu, 9 Nov 2023 15:37:45 +0900 Subject: [PATCH 21/21] test: Reimplement MockStates --- .../ActionEvaluationTest.cs | 4 +- .../Mocks/MockAccountState.cs | 57 ++++++++++--------- .../Mocks/MockWorldState.cs | 46 +++++++++------ Libplanet.Tests/Action/AccountTest.cs | 2 +- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/Libplanet.Action.Tests/ActionEvaluationTest.cs b/Libplanet.Action.Tests/ActionEvaluationTest.cs index fe799e5030d..1c0c1cfc3f6 100644 --- a/Libplanet.Action.Tests/ActionEvaluationTest.cs +++ b/Libplanet.Action.Tests/ActionEvaluationTest.cs @@ -42,9 +42,9 @@ public void Constructor() 0, false), new World( - new MockWorldState().SetAccount( + new MockWorldState().SetAccountState( ReservedAddresses.LegacyAccount, - new Account(MockAccountState.Empty.SetState(address, (Text)"item")))) + new Account(new MockAccountState().SetState(address, (Text)"item")))) ); var action = (DumbAction)evaluation.Action; diff --git a/Libplanet.Action.Tests/Mocks/MockAccountState.cs b/Libplanet.Action.Tests/Mocks/MockAccountState.cs index 78544f3de9e..5c0737696e4 100644 --- a/Libplanet.Action.Tests/Mocks/MockAccountState.cs +++ b/Libplanet.Action.Tests/Mocks/MockAccountState.cs @@ -1,12 +1,14 @@ using System.Numerics; +using System.Security.Cryptography; using Bencodex.Types; using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; using Libplanet.Store.Trie; using Libplanet.Types.Assets; -using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; +using static Libplanet.Action.State.KeyConverters; namespace Libplanet.Action.Tests.Mocks { @@ -47,34 +49,31 @@ namespace Libplanet.Action.Tests.Mocks /// public class MockAccountState : IAccountState { - private static readonly MockAccountState _empty = new MockAccountState(); + private readonly IStateStore _stateStore; public MockAccountState() - : this(new TrieStateStore(new MemoryKeyValueStore()).GetStateRoot(null)) + : this(new TrieStateStore(new MemoryKeyValueStore()), null) { } - private MockAccountState(ITrie trie) + public MockAccountState( + IStateStore stateStore, + HashDigest? stateRootHash = null) { - Trie = trie; + _stateStore = stateStore; + Trie = stateStore.GetStateRoot(stateRootHash); } - public static MockAccountState Empty => _empty; - - public Address Address { get; } - public ITrie Trie { get; } - public BlockHash? BlockHash { get; } - public IValue GetState(Address address) => - Trie.Get(KeyConverters.ToStateKey(address)); + Trie.Get(ToStateKey(address)); public IReadOnlyList GetStates(IReadOnlyList
addresses) => addresses.Select(GetState).ToList(); public FungibleAssetValue GetBalance(Address address, Currency currency) => - Trie.Get(KeyConverters.ToFungibleAssetKey(address, currency)) is Integer rawValue + Trie.Get(ToFungibleAssetKey(address, currency)) is Integer rawValue ? FungibleAssetValue.FromRawValue(currency, rawValue) : currency * 0; @@ -89,19 +88,20 @@ public FungibleAssetValue GetTotalSupply(Currency currency) throw new TotalSupplyNotTrackableException(msg, currency); } - return Trie.Get(KeyConverters.ToTotalSupplyKey(currency)) is Integer rawValue + return Trie.Get(ToTotalSupplyKey(currency)) is Integer rawValue ? FungibleAssetValue.FromRawValue(currency, rawValue) : currency * 0; } public ValidatorSet GetValidatorSet() => - Trie.Get(KeyConverters.ValidatorSetKey) is List list + Trie.Get(ValidatorSetKey) is List list ? new ValidatorSet(list) : new ValidatorSet(); public MockAccountState SetState(Address address, IValue state) => new MockAccountState( - Trie.Set(KeyConverters.ToStateKey(address), state)); + _stateStore, + _stateStore.Commit(Trie.Set(ToStateKey(address), state)).Hash); public MockAccountState SetBalance( Address address, FungibleAssetValue amount) => @@ -114,9 +114,11 @@ public MockAccountState SetBalance( public MockAccountState SetBalance( (Address Address, Currency Currency) pair, BigInteger rawAmount) => new MockAccountState( - Trie.Set( - KeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency), - new Integer(rawAmount))); + _stateStore, + _stateStore.Commit( + Trie.Set( + ToFungibleAssetKey(pair.Address, pair.Currency), + new Integer(rawAmount))).Hash); public MockAccountState AddBalance(Address address, FungibleAssetValue amount) => AddBalance((address, amount.Currency), amount.RawValue); @@ -129,7 +131,7 @@ public MockAccountState AddBalance( (Address Address, Currency Currency) pair, BigInteger rawAmount) => SetBalance( pair, - (Trie.Get(KeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency)) is + (Trie.Get(ToFungibleAssetKey(pair.Address, pair.Currency)) is Integer amount ? amount : 0) + rawAmount); public MockAccountState SubtractBalance( @@ -144,7 +146,7 @@ public MockAccountState SubtractBalance( (Address Address, Currency Currency) pair, BigInteger rawAmount) => SetBalance( pair, - (Trie.Get(KeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency)) is + (Trie.Get(ToFungibleAssetKey(pair.Address, pair.Currency)) is Integer amount ? amount : 0) - rawAmount); public MockAccountState TransferBalance( @@ -163,7 +165,9 @@ public MockAccountState SetTotalSupply(Currency currency, BigInteger rawAmount) ? !(currency.MaximumSupply is FungibleAssetValue maximumSupply) || rawAmount <= maximumSupply.RawValue ? new MockAccountState( - Trie.Set(KeyConverters.ToTotalSupplyKey(currency), new Integer(rawAmount))) + _stateStore, + _stateStore.Commit( + Trie.Set(ToTotalSupplyKey(currency), new Integer(rawAmount))).Hash) : throw new ArgumentException( $"Given {currency}'s total supply is capped at {maximumSupply.RawValue} " + $"and cannot be set to {rawAmount}.") @@ -176,7 +180,7 @@ public MockAccountState AddTotalSupply(FungibleAssetValue amount) => public MockAccountState AddTotalSupply(Currency currency, BigInteger rawAmount) => SetTotalSupply( currency, - (Trie.Get(KeyConverters.ToTotalSupplyKey(currency)) is + (Trie.Get(ToTotalSupplyKey(currency)) is Integer amount ? amount : 0) + rawAmount); public MockAccountState SubtractTotalSupply(FungibleAssetValue amount) => @@ -185,12 +189,13 @@ public MockAccountState SubtractTotalSupply(FungibleAssetValue amount) => public MockAccountState SubtractTotalSupply(Currency currency, BigInteger rawAmount) => SetTotalSupply( currency, - (Trie.Get(KeyConverters.ToTotalSupplyKey(currency)) is + (Trie.Get(ToTotalSupplyKey(currency)) is Integer amount ? amount : 0) - rawAmount); public MockAccountState SetValidator(Validator validator) => new MockAccountState( - Trie.Set( - KeyConverters.ValidatorSetKey, GetValidatorSet().Update(validator).Bencoded)); + _stateStore, + _stateStore.Commit( + Trie.Set(ValidatorSetKey, GetValidatorSet().Update(validator).Bencoded)).Hash); } } diff --git a/Libplanet.Action.Tests/Mocks/MockWorldState.cs b/Libplanet.Action.Tests/Mocks/MockWorldState.cs index 5c4f259c528..a7545671be0 100644 --- a/Libplanet.Action.Tests/Mocks/MockWorldState.cs +++ b/Libplanet.Action.Tests/Mocks/MockWorldState.cs @@ -1,30 +1,34 @@ -using System.Collections.Immutable; +using System.Security.Cryptography; +using Bencodex.Types; using Libplanet.Action.State; +using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; using Libplanet.Store.Trie; +using static Libplanet.Action.State.KeyConverters; namespace Libplanet.Action.Tests.Mocks { public class MockWorldState : IWorldState { - private readonly IImmutableDictionary _accounts; + private readonly IStateStore _stateStore; public MockWorldState() - : this(ImmutableDictionary.Empty) + : this(new TrieStateStore(new MemoryKeyValueStore()), null) { } - public MockWorldState(IImmutableDictionary accounts) - : this(new TrieStateStore(new MemoryKeyValueStore()).GetStateRoot(null), accounts) + public MockWorldState( + IStateStore stateStore, + HashDigest? stateRootHash = null) { - } - - public MockWorldState(ITrie trie, IImmutableDictionary accounts) - { - Trie = trie; - Legacy = true; - _accounts = accounts; + Trie = stateStore.GetStateRoot(stateRootHash); + Legacy = Trie + .Get(new[] + { + ToStateKey(ReservedAddresses.LegacyAccount), + }) + .Any(v => v == null); } public ITrie Trie { get; } @@ -32,11 +36,17 @@ public MockWorldState(ITrie trie, IImmutableDictionary accoun public bool Legacy { get; private set; } public IAccount GetAccount(Address address) - => _accounts.TryGetValue(address, out IAccount account) - ? account - : new Account(new MockAccountState()); - - public IWorldState SetAccount(Address address, IAccount account) => - new MockWorldState(_accounts.SetItem(address, account)); + => Legacy && address.Equals(ReservedAddresses.LegacyAccount) + ? new Account(new MockAccountState(_stateStore, Trie.Hash)) + : new Account(new MockAccountState( + _stateStore, new HashDigest(Trie.Get(ToStateKey(address))))); + + public IWorldState SetAccountState(Address address, IAccount account) + => Legacy && address.Equals(ReservedAddresses.LegacyAccount) + ? new MockWorldState(_stateStore, account.Trie.Hash) + : new MockWorldState( + _stateStore, + _stateStore.Commit( + Trie.Set(ToStateKey(address), new Binary(account.Trie.Hash.ByteArray))).Hash); } } diff --git a/Libplanet.Tests/Action/AccountTest.cs b/Libplanet.Tests/Action/AccountTest.cs index c79bc25b0d3..0ce9e29055e 100644 --- a/Libplanet.Tests/Action/AccountTest.cs +++ b/Libplanet.Tests/Action/AccountTest.cs @@ -51,7 +51,7 @@ protected AccountTest(ITestOutputHelper output) Currency.Capped("QUUX", 0, (100, 0), minter: _addr[0]), }; - _initAccount = new Account(MockAccountState.Empty + _initAccount = new Account(new MockAccountState() .SetState(_addr[0], (Text)"a") .SetState(_addr[1], (Text)"b") .SetBalance(_addr[0], _currencies[0], 5)