diff --git a/CHANGES.md b/CHANGES.md index b388556b429..780857939bd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,8 +28,6 @@ To be released. parameter `IActionEvaluator actionEvaluator` any more. [[#3811]] - (Libplanet) `BlockChain.ProposeBlock()` receives parameter `HashDigest stateRootHash`. [[#3811]] - - (Libplanet) Added `BlockChain.GetNextWorldState()` and - `BlockChain.GetNextWorldState(BlockHash?)` methods. [[#3821]] ### Backward-incompatible network protocol changes @@ -37,12 +35,6 @@ To be released. ### Added APIs - - (Libplanet.Store) Added `IStore.GetNextStateRootHash()` method. - [[#3811]] - - (Libplanet.Store) Added `IStore.PutNextStateRootHash()` method. - [[#3811]] - - (Libplanet.Store) Added `IStore.DeleteNextStateRootHash()` method. - [[#3811]] - (Libplanet) Added `BlockChain.DetermineNextBlockStateRootHash()` method. [[#3811]] @@ -51,8 +43,6 @@ To be released. - `BlockHeader.StateRootHash` now means state root hash calculated by `BlockChain.DetermineNextBlockStateRootHash(previousBlockHash)`. [[#Sloth]] - - `IBlockChainStates.GetWorldState(BlockHash)` now means the `IWorldState` - before state transition from the `Block`. [[#3811]] - `Context.ProcessHeightOrRoundUponRules()` now appends block asynchronously, as a manner of fire-and-forget. [[#3808]] diff --git a/Libplanet.Action/State/IBlockChainStates.cs b/Libplanet.Action/State/IBlockChainStates.cs index 005c32eee3c..f7dbdd62929 100644 --- a/Libplanet.Action/State/IBlockChainStates.cs +++ b/Libplanet.Action/State/IBlockChainStates.cs @@ -22,8 +22,8 @@ public interface IBlockChainStates /// The at , which is the identical /// to what of indicates. /// - /// Thrown when - /// one of the following is true. + /// Thrown when one of the following is true + /// for . /// /// /// Corresponding is not found in the . @@ -46,7 +46,7 @@ public interface IBlockChainStates /// /// The with . /// If is , - /// returns the hash of the empty state root. + /// returns an empty . /// /// Thrown when no with /// as its state root hash is found. diff --git a/Libplanet.Net.Tests/Consensus/ContextTest.cs b/Libplanet.Net.Tests/Consensus/ContextTest.cs index d66f03c7ee9..61e7cb78c36 100644 --- a/Libplanet.Net.Tests/Consensus/ContextTest.cs +++ b/Libplanet.Net.Tests/Consensus/ContextTest.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Linq; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Bencodex; using Bencodex.Types; @@ -16,6 +17,7 @@ using Libplanet.Net.Messages; using Libplanet.Store; using Libplanet.Store.Trie; +using Libplanet.Tests; using Libplanet.Tests.Store; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; @@ -321,7 +323,7 @@ void BroadcastMessage(ConsensusMsg message) => 1L, TestUtils.PrivateKeys[0], blockChain - .GetNextWorldState(blockChain[0L].Hash)! + .GetNextWorldState(0L) .GetValidatorSet(), contextTimeoutOptions: new ContextTimeoutOption()); @@ -623,7 +625,7 @@ void BroadcastMessage(ConsensusMsg message) => 1L, TestUtils.PrivateKeys[0], blockChain - .GetNextWorldState(blockChain[0L].Hash)! + .GetNextWorldState(0L) .GetValidatorSet(), contextTimeoutOptions: new ContextTimeoutOption()); @@ -687,6 +689,7 @@ void BroadcastMessage(ConsensusMsg message) => var watch = Stopwatch.StartNew(); await onTipChanged.WaitAsync(); Assert.True(watch.ElapsedMilliseconds < (actionDelay * 0.5)); + Thread.Sleep(100); // Wait for votes to get collected. Assert.Equal( 4, diff --git a/Libplanet.Net.Tests/TestUtils.cs b/Libplanet.Net.Tests/TestUtils.cs index ffe72a2c1a6..1c72f6f1fb9 100644 --- a/Libplanet.Net.Tests/TestUtils.cs +++ b/Libplanet.Net.Tests/TestUtils.cs @@ -19,6 +19,7 @@ using Libplanet.Net.Transports; using Libplanet.Store; using Libplanet.Store.Trie; +using Libplanet.Tests; using Libplanet.Tests.Store; using Libplanet.Types.Blocks; using Libplanet.Types.Consensus; @@ -288,7 +289,7 @@ void BroadcastMessage(ConsensusMsg message) => height, privateKey, validatorSet ?? blockChain - .GetNextWorldState(blockChain[height - 1].Hash)! + .GetNextWorldState(height - 1) .GetValidatorSet(), contextTimeoutOptions: contextTimeoutOptions ?? new ContextTimeoutOption()); diff --git a/Libplanet.Net/Consensus/ConsensusContext.cs b/Libplanet.Net/Consensus/ConsensusContext.cs index cb70301f067..762c0bae8e6 100644 --- a/Libplanet.Net/Consensus/ConsensusContext.cs +++ b/Libplanet.Net/Consensus/ConsensusContext.cs @@ -452,8 +452,7 @@ private Context CreateContext(long height) { // blockchain may not contain block of Height - 1? ValidatorSet validatorSet; - if (_blockChain.Tip.ProtocolVersion - < BlockMetadata.SlothProtocolVersion) + if (_blockChain.Tip.ProtocolVersion < BlockMetadata.SlothProtocolVersion) { validatorSet = _blockChain .GetWorldState(_blockChain[Height - 1].StateRootHash) @@ -461,11 +460,20 @@ private Context CreateContext(long height) } else { - validatorSet = _blockChain - .GetWorldState( - _blockChain.GetNextStateRootHash( - _blockChain[Height - 1].Hash, TimeSpan.Zero)) - .GetValidatorSet(); + while (true) + { + var nextStateRootHash = _blockChain.GetNextStateRootHash(Height - 1); + if (nextStateRootHash is { } nsrh) + { + validatorSet = _blockChain + .GetWorldState(nsrh) + .GetValidatorSet(); + break; + } + + // FIXME: Maybe this should be adjustable? + Thread.Sleep(100); + } } Context context = new Context( diff --git a/Libplanet.Net/Consensus/Context.cs b/Libplanet.Net/Consensus/Context.cs index f1698b2a6dc..cb6b1b47cb0 100644 --- a/Libplanet.Net/Consensus/Context.cs +++ b/Libplanet.Net/Consensus/Context.cs @@ -491,7 +491,7 @@ private bool IsValid(Block block) } } - _blockChain.ValidateBlockStateRootHash(block, TimeSpan.Zero); + _blockChain.ValidateBlockStateRootHash(block); } catch (Exception e) when ( e is InvalidBlockException || diff --git a/Libplanet.Store/IStore.cs b/Libplanet.Store/IStore.cs index 7edd7d4e318..d273a4bd18e 100644 --- a/Libplanet.Store/IStore.cs +++ b/Libplanet.Store/IStore.cs @@ -348,33 +348,5 @@ public interface IStore : IDisposable /// Returns an of es /// of all s. IEnumerable GetBlockCommitHashes(); - - /// - /// Gets the next state root hash for given from the store. - /// - /// The of the - /// to retrieve next state root hash. - /// Returns if next state root hash of given - /// is stored and available, - /// otherwise returns . - HashDigest? GetNextStateRootHash(BlockHash blockHash); - - /// - /// Puts a next state root hash of corresponding to the - /// to the store. - /// - /// The of the - /// where evaluated. - /// The that represents - /// state root hash of the next state. - void PutNextStateRootHash(BlockHash blockHash, HashDigest nextStateRootHash); - - /// - /// Deletes the next state root hash of corresponding to the given - /// from the store. - /// - /// The of a next state root hash - /// to delete. - void DeleteNextStateRootHash(BlockHash blockHash); } } diff --git a/Libplanet.Store/MemoryStore.cs b/Libplanet.Store/MemoryStore.cs index e394b0e266d..43b3a7b916b 100644 --- a/Libplanet.Store/MemoryStore.cs +++ b/Libplanet.Store/MemoryStore.cs @@ -301,20 +301,6 @@ public void DeleteBlockCommit(BlockHash blockHash) => public IEnumerable GetBlockCommitHashes() => _blockCommits.Keys; - /// - public HashDigest? GetNextStateRootHash(BlockHash blockHash) - => _nextStateRootHashes.TryGetValue(blockHash, out var nextStateRootHash) - ? (HashDigest?)nextStateRootHash - : null; - - /// - public void PutNextStateRootHash(BlockHash blockHash, HashDigest nextStateRootHash) - => _nextStateRootHashes[blockHash] = nextStateRootHash; - - /// - public void DeleteNextStateRootHash(BlockHash blockHash) - => _nextStateRootHashes.TryRemove(blockHash, out _); - [StoreLoader("memory")] private static (IStore Store, IStateStore StateStore) Loader(Uri storeUri) { diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.Migration.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.Migration.cs index 08035590b2c..0cbec15057a 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.Migration.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.Migration.cs @@ -332,7 +332,7 @@ public void MigrateThroughBlockWorldState() var block3 = chain.ProposeBlock(miner, blockCommit); chain.Append(block3, CreateBlockCommit(block3)); Assert.Equal(BlockMetadata.CurrentProtocolVersion, chain.GetNextWorldState().Version); - var accountStateRoot = stateStore.GetStateRoot(store.GetNextStateRootHash(block3.Hash)) + var accountStateRoot = stateStore.GetStateRoot(chain.GetNextStateRootHash(block3.Hash)) .Get(KeyConverters.ToStateKey(ModernAction.AccountAddress)); Assert.NotNull(accountStateRoot); var accountTrie = stateStore.GetStateRoot(new HashDigest(accountStateRoot)); diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index 22fc632c1fa..acb72c551d7 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -1009,7 +1009,7 @@ public void EvaluateActionAndCollectFee() Block block = chain.ProposeBlock(miner); var evaluations = chain.ActionEvaluator.Evaluate( - block, chain.Store.GetNextStateRootHash((BlockHash)block.PreviousHash)); + block, chain.GetNextStateRootHash((BlockHash)block.PreviousHash)); Assert.False(evaluations[0].InputContext.BlockAction); Assert.Single(evaluations); @@ -1079,7 +1079,7 @@ public void EvaluateThrowingExceedGasLimit() var evaluations = chain.ActionEvaluator.Evaluate( block, - chain.Store.GetNextStateRootHash((BlockHash)block.PreviousHash)); + chain.GetNextStateRootHash((BlockHash)block.PreviousHash)); Assert.False(evaluations[0].InputContext.BlockAction); Assert.Single(evaluations); diff --git a/Libplanet.Tests/Action/WorldTest.cs b/Libplanet.Tests/Action/WorldTest.cs index 1598fcb52d0..a1a58018fb2 100644 --- a/Libplanet.Tests/Action/WorldTest.cs +++ b/Libplanet.Tests/Action/WorldTest.cs @@ -260,11 +260,13 @@ public virtual void TransferAssetInBlock() chain.Append(block1, TestUtils.CreateBlockCommit(block1)); Assert.Equal( DumbAction.DumbCurrency * 0, - GetLatestWorldState(chain) + chain + .GetNextWorldState() .GetBalance(_addr[0], DumbAction.DumbCurrency)); Assert.Equal( DumbAction.DumbCurrency * 20, - GetLatestWorldState(chain) + chain + .GetNextWorldState() .GetBalance(_addr[1], DumbAction.DumbCurrency)); // Transfer @@ -288,11 +290,13 @@ public virtual void TransferAssetInBlock() chain.Append(block2, TestUtils.CreateBlockCommit(block2)); Assert.Equal( DumbAction.DumbCurrency * 5, - GetLatestWorldState(chain) + chain + .GetNextWorldState() .GetBalance(_addr[0], DumbAction.DumbCurrency)); Assert.Equal( DumbAction.DumbCurrency * 15, - GetLatestWorldState(chain) + chain + .GetNextWorldState() .GetBalance(_addr[1], DumbAction.DumbCurrency)); // Transfer bugged @@ -504,11 +508,6 @@ public virtual void TotalSupplyTracking() world.GetTotalSupply(_currencies[4])); } - protected static IWorldState GetLatestWorldState(BlockChain blockChain) => - blockChain.Tip.ProtocolVersion < BlockMetadata.SlothProtocolVersion - ? blockChain.GetWorldState() - : blockChain.GetNextWorldState(); - protected FungibleAssetValue Value(int currencyIndex, BigInteger quantity) => new FungibleAssetValue(_currencies[currencyIndex], quantity, 0); } diff --git a/Libplanet.Tests/BlockChainExtensions.cs b/Libplanet.Tests/BlockChainExtensions.cs new file mode 100644 index 00000000000..a434832f9c6 --- /dev/null +++ b/Libplanet.Tests/BlockChainExtensions.cs @@ -0,0 +1,53 @@ +using System.Security.Cryptography; +using Libplanet.Action.State; +using Libplanet.Blockchain; +using Libplanet.Common; +using Libplanet.Types.Blocks; + +namespace Libplanet.Tests +{ + public static class BlockChainExtensions + { + /// + /// Returns an resulting from the execution of + /// the tip of . + /// + /// The to search. + /// An resulting from the execution of + /// the tip of . + public static IWorldState GetNextWorldState(this BlockChain blockChain) => + blockChain.GetNextStateRootHash() is HashDigest stateRootHash + ? blockChain.GetWorldState(stateRootHash) + : null; + + /// + /// Returns an resulting from the execution of + /// a associated with given + /// from . + /// + /// The to search. + /// The index of a to search. + /// An resulting from the execution of + /// a associated with given . + public static IWorldState GetNextWorldState(this BlockChain blockChain, long index) => + blockChain.GetNextStateRootHash(index) is HashDigest stateRootHash + ? blockChain.GetWorldState(stateRootHash) + : null; + + /// + /// Returns an resulting from the execution of + /// a associated with given + /// from . + /// + /// The to search. + /// The to search. + /// An resulting from the execution of + /// a associated with given . + public static IWorldState GetNextWorldState( + this BlockChain blockChain, + BlockHash blockHash) => + blockChain.GetNextStateRootHash(blockHash) is HashDigest stateRootHash + ? blockChain.GetWorldState(stateRootHash) + : null; + } +} diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs index 60d0427fd87..caa940dc057 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Internals.cs @@ -105,8 +105,7 @@ Transaction MkTx(PrivateKey key, long nonce, DateTimeOffset? ts = null) => [SkippableFact] public void ExecuteActions() { - (var addresses, Transaction[] txs) = - MakeFixturesForAppendTests(); + (var addresses, Transaction[] txs) = MakeFixturesForAppendTests(); var genesis = _blockChain.Genesis; Block block1 = _blockChain.ProposeBlock( @@ -126,7 +125,7 @@ public void ExecuteActions() }; IValue legacyStateRootRaw = - _fx.StateStore.GetStateRoot(_fx.Store.GetNextStateRootHash(block1.Hash)) + _fx.StateStore.GetStateRoot(_blockChain.GetNextStateRootHash()) .Get(ToStateKey(ReservedAddresses.LegacyAccount)); Assert.NotNull(legacyStateRootRaw); var legacyStateRoot = diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index 781103239c3..819eeef1b4d 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -1862,8 +1862,6 @@ void BuildIndex(Guid id, Block block) .ToList(); Assert.NotEmpty(dirty); store.PutBlock(b); - store.PutNextStateRootHash( - b.Hash, evals.Last().OutputState.Trie.Hash); BuildIndex(chain.Id, b); Assert.Equal(b, chain[b.Hash]); if (presentIndices.Contains((int)b.Index)) @@ -2316,7 +2314,7 @@ private void ValidateNextBlockCommitOnValidatorSetChange() Assert.Equal( blockChain - .GetNextWorldState(blockChain[0].Hash) + .GetNextWorldState(0L) .GetValidatorSet(), new ValidatorSet( ValidatorPrivateKeys.Select( @@ -2324,7 +2322,7 @@ private void ValidateNextBlockCommitOnValidatorSetChange() Assert.Equal( blockChain - .GetNextWorldState(blockChain[1].Hash) + .GetNextWorldState(1L) .GetValidatorSet(), new ValidatorSet( newValidators.Select( diff --git a/Libplanet.Tests/Store/ProxyStore.cs b/Libplanet.Tests/Store/ProxyStore.cs index cd384d6adb2..dde51f23d54 100644 --- a/Libplanet.Tests/Store/ProxyStore.cs +++ b/Libplanet.Tests/Store/ProxyStore.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Security.Cryptography; -using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; using Libplanet.Types.Blocks; @@ -191,17 +189,5 @@ public void DeleteBlockCommit(BlockHash blockHash) => /// public IEnumerable GetBlockCommitHashes() => Store.GetBlockCommitHashes(); - - /// - public HashDigest? GetNextStateRootHash(BlockHash blockHash) => - Store.GetNextStateRootHash(blockHash); - - /// - public void PutNextStateRootHash(BlockHash blockHash, HashDigest nextStateRootHash) - => Store.PutNextStateRootHash(blockHash, nextStateRootHash); - - /// - public void DeleteNextStateRootHash(BlockHash blockHash) => - Store.DeleteNextStateRootHash(blockHash); } } diff --git a/Libplanet.Tests/Store/StoreTest.cs b/Libplanet.Tests/Store/StoreTest.cs index 3adb0d9fd72..1e8f114749e 100644 --- a/Libplanet.Tests/Store/StoreTest.cs +++ b/Libplanet.Tests/Store/StoreTest.cs @@ -1169,37 +1169,6 @@ public void DeleteLastCommit() } } - [SkippableFact] - public void GetNextStateRootHash() - { - using (StoreFixture fx = FxConstructor()) - { - HashDigest nextStateRootHash - = HashDigest.DeriveFrom(new byte[] { 0, 1, 2 }); - fx.Store.PutNextStateRootHash(fx.Block2.Hash, nextStateRootHash); - HashDigest? storedNextStateRootHash = - fx.Store.GetNextStateRootHash(fx.Block2.Hash); - - Assert.Equal(nextStateRootHash, storedNextStateRootHash); - } - } - - [SkippableFact] - public void DeleteNextStateRootHash() - { - using (StoreFixture fx = FxConstructor()) - { - HashDigest nextStateRootHash - = HashDigest.DeriveFrom(new byte[] { 0, 1, 2 }); - fx.Store.PutNextStateRootHash(fx.Block2.Hash, nextStateRootHash); - - Assert.NotNull(fx.Store.GetNextStateRootHash(fx.Block2.Hash)); - - fx.Store.DeleteNextStateRootHash(fx.Block2.Hash); - Assert.Null(fx.Store.GetNextStateRootHash(fx.Block2.Hash)); - } - } - [SkippableFact] public void ForkTxNonces() { diff --git a/Libplanet.Tests/Store/StoreTracker.cs b/Libplanet.Tests/Store/StoreTracker.cs index f6df855f1c1..c2faff600b1 100644 --- a/Libplanet.Tests/Store/StoreTracker.cs +++ b/Libplanet.Tests/Store/StoreTracker.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Security.Cryptography; -using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store; using Libplanet.Types.Blocks; @@ -232,24 +230,6 @@ public IEnumerable GetBlockCommitHashes() return _store.GetBlockCommitHashes(); } - public HashDigest? GetNextStateRootHash(BlockHash blockHash) - { - Log(nameof(GetNextStateRootHash)); - return _store.GetNextStateRootHash(blockHash); - } - - public void PutNextStateRootHash(BlockHash blockHash, HashDigest nextStateRootHash) - { - Log(nameof(PutNextStateRootHash)); - _store.PutNextStateRootHash(blockHash, nextStateRootHash); - } - - public void DeleteNextStateRootHash(BlockHash blockHash) - { - Log(nameof(DeleteNextStateRootHash)); - _store.DeleteNextStateRootHash(blockHash); - } - public Guid? GetCanonicalChainId() { Log(nameof(GetCanonicalChainId)); diff --git a/Libplanet.Tests/TestUtils.cs b/Libplanet.Tests/TestUtils.cs index 933f9476d2e..2f8186d85df 100644 --- a/Libplanet.Tests/TestUtils.cs +++ b/Libplanet.Tests/TestUtils.cs @@ -626,7 +626,7 @@ public static (BlockChain BlockChain, ActionEvaluator ActionEvaluator) timestamp, protocolVersion); var evaluatedSrh = actionEvaluator.Evaluate(preEval, null).Last().OutputState; - genesisBlock = protocolVersion < 2 + genesisBlock = protocolVersion < BlockMetadata.SignatureProtocolVersion ? new Block( preEval, ( @@ -635,8 +635,8 @@ public static (BlockChain BlockChain, ActionEvaluator ActionEvaluator) preEval.Header.DeriveBlockHash(evaluatedSrh, null) )) : protocolVersion < BlockMetadata.SlothProtocolVersion - ? preEval.Sign(GenesisProposer, evaluatedSrh) - : preEval.Sign(GenesisProposer, MerkleTrie.EmptyRootHash); + ? preEval.Sign(GenesisProposer, evaluatedSrh) + : preEval.Sign(GenesisProposer, MerkleTrie.EmptyRootHash); } ValidatingActionRenderer validator = null; diff --git a/Libplanet/Blockchain/BlockChain.Evaluate.cs b/Libplanet/Blockchain/BlockChain.Evaluate.cs index 7dc56aea34a..3abb563cc6c 100644 --- a/Libplanet/Blockchain/BlockChain.Evaluate.cs +++ b/Libplanet/Blockchain/BlockChain.Evaluate.cs @@ -113,48 +113,38 @@ public IReadOnlyList EvaluateBlock(Block block) => internal Block EvaluateAndSign( PreEvaluationBlock preEvaluationBlock, PrivateKey privateKey) { - if (preEvaluationBlock.Index < 1) + if (preEvaluationBlock.ProtocolVersion < BlockMetadata.SlothProtocolVersion) { - throw new ArgumentException( - $"Given {nameof(preEvaluationBlock)} must have block index " + - $"higher than 0"); - } - - if (preEvaluationBlock.ProtocolVersion < BlockMetadata.SignatureProtocolVersion) - { - throw new ArgumentException( - $"Given {nameof(preEvaluationBlock)} must have protocol version " + - $"2 or greater: {preEvaluationBlock.ProtocolVersion}"); - } - - if (preEvaluationBlock.ProtocolVersion < - BlockMetadata.SlothProtocolVersion) - { - return preEvaluationBlock.Sign( - privateKey, - DetermineBlockPrecededStateRootHash(preEvaluationBlock, out _)); - } - - if (!(preEvaluationBlock.PreviousHash is BlockHash previousHash)) - { - throw new ArgumentException( - $"Given {nameof(preEvaluationBlock)} must have non-null previous hash " + - $"If it isn't genesis"); - } - - if (_blocks[previousHash].ProtocolVersion - < BlockMetadata.SlothProtocolVersion) - { - return preEvaluationBlock.Sign(privateKey, _blocks[previousHash].StateRootHash); + if (preEvaluationBlock.ProtocolVersion < BlockMetadata.SignatureProtocolVersion) + { + throw new ArgumentException( + $"Given {nameof(preEvaluationBlock)} must have protocol version " + + $"2 or greater: {preEvaluationBlock.ProtocolVersion}"); + } + else + { + return preEvaluationBlock.Sign( + privateKey, + DetermineBlockPrecededStateRootHash(preEvaluationBlock, out _)); + } } - - if (!(GetNextStateRootHash(previousHash) is HashDigest stateRootHash)) + else { - throw new NullReferenceException( - $"State root hash of block is not prepared"); + if (preEvaluationBlock.Index < 1) + { + throw new ArgumentException( + $"Given {nameof(preEvaluationBlock)} must have block index " + + $"higher than 0"); + } + else + { + var prevBlock = _blocks[(BlockHash)preEvaluationBlock.PreviousHash]; + var stateRootHash = GetNextStateRootHash(prevBlock.Hash) + ?? throw new NullReferenceException( + $"State root hash of block is not prepared"); + return preEvaluationBlock.Sign(privateKey, stateRootHash); + } } - - return preEvaluationBlock.Sign(privateKey, stateRootHash); } /// diff --git a/Libplanet/Blockchain/BlockChain.ProposeBlock.cs b/Libplanet/Blockchain/BlockChain.ProposeBlock.cs index e831b2f55bb..09ef70d94be 100644 --- a/Libplanet/Blockchain/BlockChain.ProposeBlock.cs +++ b/Libplanet/Blockchain/BlockChain.ProposeBlock.cs @@ -145,11 +145,10 @@ internal Block ProposeBlock( BlockHash prevHash = Store.IndexBlockHash(Id, index - 1) ?? throw new NullReferenceException($"Chain {Id} is missing block #{index - 1}"); - HashDigest stateRootHash = - _blocks[prevHash].ProtocolVersion < - BlockMetadata.SlothProtocolVersion - ? _blocks[prevHash].StateRootHash - : (HashDigest)GetNextStateRootHash(prevHash, TimeSpan.Zero); + HashDigest stateRootHash = GetNextStateRootHash(prevHash) ?? + throw new InvalidOperationException( + $"Cannot propose a block as the next state root hash " + + $"for block {prevHash} is missing."); // FIXME: Should use automated public constructor. // Manual internal constructor is used purely for testing custom timestamps. diff --git a/Libplanet/Blockchain/BlockChain.States.cs b/Libplanet/Blockchain/BlockChain.States.cs index efa27efbec7..e55a4d35cc1 100644 --- a/Libplanet/Blockchain/BlockChain.States.cs +++ b/Libplanet/Blockchain/BlockChain.States.cs @@ -2,7 +2,6 @@ using System.Security.Cryptography; using Libplanet.Action.State; using Libplanet.Common; -using Libplanet.Store; using Libplanet.Types.Blocks; namespace Libplanet.Blockchain @@ -27,33 +26,16 @@ public IWorldState GetWorldState(HashDigest? stateRootHash) /// Gets the next world state in the . /// /// The next world state. If it does not exist, returns null. - public IWorldState? GetNextWorldState() => GetNextWorldState(Tip.Hash); - - /// - /// Returns the next in the BlockChain - /// at . - /// - /// The of the to create - /// for which to create an . - /// - /// The next at . - /// Returns if next state root hash does not exists. - /// - /// Thrown when next state root hash exists, - /// but corresponding state root is not found in the . - /// - /// - public IWorldState? GetNextWorldState(BlockHash offset) + public IWorldState? GetNextWorldState() { - var nextSrh = Store.GetNextStateRootHash(offset); - if (nextSrh is { } srh) + if (GetNextStateRootHash() is { } nsrh) { - var trie = StateStore.GetStateRoot(srh); + var trie = StateStore.GetStateRoot(nsrh); return trie.Recorded - ? new WorldBaseState(StateStore.GetStateRoot(nextSrh), StateStore) - : throw new ArgumentException( - $"Could not find state root {srh} in {nameof(StateStore)}.", - nameof(offset)); + ? new WorldBaseState(trie, StateStore) + : throw new InvalidOperationException( + $"Could not find state root {nsrh} in {nameof(StateStore)} for " + + $"the current tip."); } else { diff --git a/Libplanet/Blockchain/BlockChain.Swap.cs b/Libplanet/Blockchain/BlockChain.Swap.cs index 0643a519044..ca35491bb9f 100644 --- a/Libplanet/Blockchain/BlockChain.Swap.cs +++ b/Libplanet/Blockchain/BlockChain.Swap.cs @@ -109,6 +109,7 @@ HashSet GetTxIdsWithRange( TipChanged?.Invoke(this, (oldTip, newTip)); Store.DeleteChainId(obsoleteId); + _nextStateRootHash = other._nextStateRootHash; } finally { diff --git a/Libplanet/Blockchain/BlockChain.Validate.cs b/Libplanet/Blockchain/BlockChain.Validate.cs index 803c5bdd468..eb3d1dc6e87 100644 --- a/Libplanet/Blockchain/BlockChain.Validate.cs +++ b/Libplanet/Blockchain/BlockChain.Validate.cs @@ -140,11 +140,9 @@ internal void ValidateBlockCommit( // FIXME: When the dynamic validator set is possible, the functionality of this // condition should be checked once more. - var validators = - block.ProtocolVersion < - BlockMetadata.SlothProtocolVersion - ? GetWorldState(block.PreviousHash ?? Genesis.Hash).GetValidatorSet() - : GetWorldState(block.StateRootHash).GetValidatorSet(); + var validators = block.ProtocolVersion < BlockMetadata.SlothProtocolVersion + ? GetWorldState(block.PreviousHash ?? Genesis.Hash).GetValidatorSet() + : GetWorldState(block.StateRootHash).GetValidatorSet(); if (!validators.ValidateBlockCommitValidators(blockCommit)) { throw new InvalidBlockCommitException( @@ -318,19 +316,21 @@ internal void ValidateBlock(Block block) /// /// Validates a result obtained from by - /// comparing the state root hash from + /// comparing the state root hash from /// which stores state root hash from , /// to the one in . /// /// The to validate against. - /// The interval for fetching calculated state root hash - /// from . + /// If this method is called + /// when the result of + /// is . This can happen if + /// is an index higher than the tip but its result is not ready yet. /// If the state root hash /// calculated by committing to the does not match /// the 's . /// /// - internal void ValidateBlockStateRootHash(Block block, TimeSpan validationInterval = default) + internal void ValidateBlockStateRootHash(Block block) { // NOTE: Since previous hash validation is on block validation, // assume block is genesis if previous hash is null. @@ -339,11 +339,10 @@ internal void ValidateBlockStateRootHash(Block block, TimeSpan validationInterva return; } - HashDigest stateRootHash = - _blocks[previousHash].ProtocolVersion < - BlockMetadata.SlothProtocolVersion - ? _blocks[previousHash].StateRootHash - : (HashDigest)GetNextStateRootHash(previousHash, validationInterval); + HashDigest stateRootHash = GetNextStateRootHash(previousHash) ?? + throw new InvalidOperationException( + $"Cannot validate a block' state root hash as the next " + + $"state root hash for block {previousHash} is missing."); if (!stateRootHash.Equals(block.StateRootHash)) { diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 49d387bf934..50b69331eba 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -64,6 +64,8 @@ public partial class BlockChain : IBlockChainStates /// private Block _genesis; + private HashDigest? _nextStateRootHash; + /// /// Initializes a new instance of the class by loading /// the canonical chain from given . @@ -185,18 +187,15 @@ private BlockChain( if (Tip.ProtocolVersion < BlockMetadata.SlothProtocolVersion) { - return; + _nextStateRootHash = Tip.StateRootHash; + } + else + { + _nextStateRootHash = + DetermineNextBlockStateRootHash(Tip, out var actionEvaluations); + IEnumerable txExecutions = MakeTxExecutions(Tip, actionEvaluations); + UpdateTxExecutions(txExecutions); } - - HashDigest nextStateRootHash = - DetermineNextBlockStateRootHash(Tip, out var actionEvaluations); - - Store.DeleteNextStateRootHash(Tip.Hash); - Store.PutNextStateRootHash(Tip.Hash, nextStateRootHash); - - IEnumerable txExecutions = - MakeTxExecutions(Tip, actionEvaluations); - UpdateTxExecutions(txExecutions); } ~BlockChain() @@ -390,8 +389,7 @@ public static BlockChain Create( var id = Guid.NewGuid(); - if (genesisBlock.ProtocolVersion < - BlockMetadata.SlothProtocolVersion) + if (genesisBlock.ProtocolVersion < BlockMetadata.SlothProtocolVersion) { var preEval = new PreEvaluationBlock( genesisBlock.Header, genesisBlock.Transactions); @@ -545,10 +543,11 @@ public void Append( if (block.ProtocolVersion < BlockMetadata.SlothProtocolVersion) { AppendStateRootHashPreceded(block, blockCommit, render: true); - return; } - - Append(block, blockCommit, render: true); + else + { + Append(block, blockCommit, render: true); + } } /// @@ -757,23 +756,13 @@ public BlockChain Fork(BlockHash point, bool inheritRenderers = true) _rwlock.EnterReadLock(); Store.AppendIndex(forkedId, Genesis.Hash); - var forked = new BlockChain( - Policy, - StagePolicy, - Store, - StateStore, - forkedId, - Genesis, - _blockChainStates, - ActionEvaluator, - renderers); Store.ForkBlockIndexes(Id, forkedId, point); if (GetBlockCommit(point) is { } p) { Store.PutChainBlockCommit(forkedId, GetBlockCommit(point)); } - Store.ForkTxNonces(Id, forked.Id); + Store.ForkTxNonces(Id, forkedId); for (Block block = Tip; block.PreviousHash is { } hash && !block.Hash.Equals(point); block = _blocks[hash]) @@ -785,10 +774,21 @@ public BlockChain Fork(BlockHash point, bool inheritRenderers = true) foreach ((Address address, int txCount) in signers) { - Store.IncreaseTxNonce(forked.Id, address, -txCount); + Store.IncreaseTxNonce(forkedId, address, -txCount); } } + var forked = new BlockChain( + Policy, + StagePolicy, + Store, + StateStore, + forkedId, + Genesis, + _blockChainStates, + ActionEvaluator, + renderers); + _logger.Information( "Forked chain at #{Index} {Hash} from id {PreviousId} to id {ForkedId}", pointBlock.Index, @@ -984,6 +984,7 @@ internal void Append( } Store.AppendIndex(Id, block.Hash); + _nextStateRootHash = null; } finally { @@ -1026,8 +1027,7 @@ internal void Append( HashDigest nextStateRootHash = DetermineNextBlockStateRootHash(block, out var actionEvaluations); - - Store.PutNextStateRootHash(block.Hash, nextStateRootHash); + _nextStateRootHash = nextStateRootHash; IEnumerable txExecutions = MakeTxExecutions(block, actionEvaluations); @@ -1159,9 +1159,6 @@ internal void AppendStateRootHashPreceded( TimestampFormat, CultureInfo.InvariantCulture)); _blocks[block.Hash] = block; - IEnumerable txExecutions = - MakeTxExecutions(block, actionEvaluations); - UpdateTxExecutions(txExecutions); foreach (KeyValuePair pair in nonceDeltas) { @@ -1179,6 +1176,10 @@ internal void AppendStateRootHashPreceded( } Store.AppendIndex(Id, block.Hash); + _nextStateRootHash = block.StateRootHash; + IEnumerable txExecutions = + MakeTxExecutions(block, actionEvaluations); + UpdateTxExecutions(txExecutions); } finally { @@ -1403,25 +1404,27 @@ internal void CleanupBlockCommitStore(long limit) } } - internal HashDigest? GetNextStateRootHash( - BlockHash blockHash, - TimeSpan? fetchInterval = null) + internal HashDigest? GetNextStateRootHash() => _nextStateRootHash; + + internal HashDigest? GetNextStateRootHash(long index) => + GetNextStateRootHash(this[index]); + + internal HashDigest? GetNextStateRootHash(BlockHash blockHash) => + GetNextStateRootHash(this[blockHash]); + + private HashDigest? GetNextStateRootHash(Block block) { - while (true) + if (block.ProtocolVersion < BlockMetadata.SlothProtocolVersion) { - if (Store.GetNextStateRootHash(blockHash) is HashDigest srh) - { - return srh; - } - - if (fetchInterval is TimeSpan interval) - { - Thread.Sleep(interval); - } - else - { - return null; - } + return block.StateRootHash; + } + else if (block.Index < Tip.Index) + { + return this[block.Index + 1].StateRootHash; + } + else + { + return GetNextStateRootHash(); } } }