From 461e0a4caa8ad0836b8a08ef5c0ba05a6fa3a737 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Fri, 18 Nov 2022 17:43:52 +0900 Subject: [PATCH 1/8] feat/blockCommit: validate given block --- CHANGES.md | 7 +++ Libplanet/Blockchain/BlockChain.cs | 78 +++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8e5cff0a12..5859c60de2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -59,6 +59,7 @@ Version PBFT - Added `BlockMarshaler.UnmarshalBlockHash()` method. [[#PBFT]] - Added `BlockChain.GetBlockCommit()` method. [[#PBFT]] - Added `BlockChain.CleanupBlockCommitStore()` method. [[#PBFT]] + - Added `BlockChain.ValidateBlockCommit()` method [[#PBFT]] - (Libplanet.Net) Added `IReactor` interface. [[#PBFT]] - (Libplanet.Net) Added `ConsensusReactor` class which inherits `IReactor` interface. [[#PBFT]] @@ -88,6 +89,12 @@ Version PBFT - `PreEvaluationBlockHeader()` constructor became to throw `InvalidBlockLastCommitException` when its metadata's `LastCommit` is invalid. [[#PBFT]] + - `BlockChain.Append()` has new parameter `BlockCommit blockCommit`, which + is a set of commits for given block. `BlockCommit` is used for checks + whether a block is committed in consensus. [[#PBFT]] + - `BlockChain.Append()` method became to throw + `InvalidBlockCommitException` when the given `BlockCommit` is invalid with + given block. [[#PBFT]] ### Bug fixes diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 0c6c471d8e..2f50084449 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -717,6 +717,8 @@ public TxExecution GetTxExecution(BlockHash blockHash, TxId txid) => /// is different from /// result of the /// . + /// Thrown when the given + /// and is invalid. public void Append( Block block, BlockCommit blockCommit, @@ -1233,6 +1235,17 @@ internal void Append( throw ibe; } + InvalidBlockCommitException ibce = ValidateBlockCommit(block, blockCommit); + + if (!(ibce is null)) + { + _logger.Error( + ibce, + "Failed to append block {BlockHash} due to invalid blockCommit.", + block.Hash); + throw ibce; + } + var nonceDeltas = new Dictionary(); foreach (Transaction tx1 in block.Transactions.OrderBy(tx => tx.Nonce)) @@ -1310,8 +1323,6 @@ internal void Append( Store.PutTxIdBlockHashIndex(tx.Id, block.Hash); } - // FIXME: Checks given BlockCommit is belong to block. Also BlockCommit is not - // stored if value is null in temporary measure. // Note: Genesis block is not committed by PBFT consensus, so it has no its // blockCommit. if (block.Index != 0 && blockCommit is { }) @@ -1540,6 +1551,69 @@ internal IEnumerable IterateBlockHashes(int offset = 0, int? limit = } } +#pragma warning disable SA1202 + public InvalidBlockCommitException ValidateBlockCommit( + Block block, + BlockCommit blockCommit) +#pragma warning restore SA1202 + { + if (block.ProtocolVersion <= BlockMetadata.PoWProtocolVersion) + { + if (blockCommit != null) + { + return new InvalidBlockCommitException( + "PoW Block doesn't have blockCommit."); + } + else + { + // To allow the PoW block to be appended, we skips the validation. + return null; + } + } + + if (block.Index == 0) + { + if (blockCommit == null) + { + return null; + } + + return new InvalidBlockCommitException( + "Genesis block does not have blockCommit."); + } + + if (block.Index != 0 && blockCommit == null) + { + return new InvalidBlockCommitException( + $"Block #{block.Hash} BlockCommit is required except for the genesis block."); + } + + if (block.Index != blockCommit.Height) + { + return new InvalidBlockCommitException( + "BlockCommit has height value that is not same with block index. " + + $"Block index is {block.Index}, however, BlockCommit height is " + + $"{blockCommit.Height}."); + } + + if (!block.Hash.Equals(blockCommit.BlockHash)) + { + return new InvalidBlockCommitException( + $"BlockCommit has different block. Block hash is {block.Hash}, " + + $"however, BlockCommit block hash is {blockCommit.BlockHash}."); + } + + // FIXME: When the dynamic validator set is possible, the functionality of this + // condition should be checked once more. + if (!Policy.GetValidatorSet(block.Index).ValidateBlockCommitValidators(blockCommit)) + { + return new InvalidBlockCommitException( + "BlockCommit has different validator set with policy's validator set."); + } + + return null; + } + #pragma warning disable SA1202 public InvalidBlockException ValidateNextBlock(Block block) #pragma warning restore SA1202 From 2e5fe59feb5c429388b4d069d8272cd37e451da1 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Fri, 18 Nov 2022 17:49:36 +0900 Subject: [PATCH 2/8] chore/blockCommit: reorder `CleanupBlockCommitStore()` --- Libplanet/Blockchain/BlockChain.cs | 52 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index 2f50084449..e22d48fb37 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -1551,6 +1551,32 @@ internal IEnumerable IterateBlockHashes(int offset = 0, int? limit = } } + /// + /// Clean up s in the store. The height + /// of will not be removed. If the stored + /// count is not over , the removal + /// is skipped. + /// + /// A exceptional index that is not to be removed. + /// A maximum count value of cache. + /// + internal void CleanupBlockCommitStore(long except, long maxCacheSize = 30) + { + IEnumerable indices = Store.GetBlockCommitIndices().ToArray(); + + if (indices.Count() < maxCacheSize) + { + return; + } + + _logger.Debug("Removing old BlockCommit caches except {Except}...", except); + + foreach (var height in indices.Except(new[] { except })) + { + Store.DeleteBlockCommit(height); + } + } + #pragma warning disable SA1202 public InvalidBlockCommitException ValidateBlockCommit( Block block, @@ -1728,31 +1754,5 @@ public InvalidBlockException ValidateNextBlock(Block block) return null; } - - /// - /// Clean up s in the store. The height - /// of will not be removed. If the stored - /// count is not over , the removal - /// is skipped. - /// - /// A exceptional index that is not to be removed. - /// A maximum count value of cache. - /// - internal void CleanupBlockCommitStore(long except, long maxCacheSize = 30) - { - IEnumerable indices = Store.GetBlockCommitIndices().ToArray(); - - if (indices.Count() < maxCacheSize) - { - return; - } - - _logger.Debug("Removing old BlockCommit caches except {Except}...", except); - - foreach (var height in indices.Except(new[] { except })) - { - Store.DeleteBlockCommit(height); - } - } } } From b6671d4da93490f2633a05484a58c49a7ef11c06 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Thu, 17 Nov 2022 20:16:53 +0900 Subject: [PATCH 3/8] chore: fix tests --- Libplanet.Tests/Action/ActionEvaluatorTest.cs | 9 ++++++--- .../Blockchain/BlockChainTest.Append.cs | 3 ++- .../Blockchain/BlockChainTest.MineBlock.cs | 4 +++- .../BlockChainTest.ValidateNextBlock.cs | 6 ++++-- Libplanet.Tests/Blockchain/BlockChainTest.cs | 15 ++++++++++----- Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs | 6 ++++-- 6 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index 58f74c4386..ccd7e70370 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -126,7 +126,8 @@ public void Evaluate() var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var chain = TestUtils.MakeBlockChain( - policy: new BlockPolicy(), + policy: new BlockPolicy( + getValidatorSet: idx => ValidatorSet), store: store, stateStore: stateStore); var tx = Transaction.Create( @@ -164,7 +165,8 @@ public void EvaluateWithException() var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var chain = TestUtils.MakeBlockChain( - policy: new BlockPolicy(), + policy: new BlockPolicy( + getValidatorSet: idx => ValidatorSet), store: store, stateStore: stateStore); var tx = Transaction.Create( @@ -1099,7 +1101,8 @@ public void OrderTxsForEvaluation( private void CheckGenesisHashInAction() { var chain = MakeBlockChain( - policy: new BlockPolicy(), + policy: new BlockPolicy( + getValidatorSet: idx => ValidatorSet), store: _storeFx.Store, stateStore: _storeFx.StateStore); var privateKey = new PrivateKey(); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs index afc1386a3f..87e52e8de5 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.Append.cs @@ -398,7 +398,8 @@ public void AppendWithoutEvaluateActions() [Fact] public void AppendWhenActionEvaluationFailed() { - var policy = new NullBlockPolicy(); + var policy = new NullBlockPolicy( + getValidatorSet: idx => TestUtils.ValidatorSet); var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs index fc9621a77b..b0e4bd15c9 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.MineBlock.cs @@ -235,7 +235,9 @@ TxPolicyViolationException IsSignerValid( : new TxPolicyViolationException("invalid signer", tx.Id); } - var policy = new BlockPolicy(validateNextBlockTx: IsSignerValid); + var policy = new BlockPolicy( + validateNextBlockTx: IsSignerValid, + getValidatorSet: idx => TestUtils.ValidatorSet); using (var fx = new MemoryStoreFixture()) { var blockChain = new BlockChain( diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs index 27f508a367..f86170daaa 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs @@ -159,7 +159,8 @@ public void ValidateNextBlockInvalidStateRootHash() { IKeyValueStore stateKeyValueStore = new MemoryKeyValueStore(); var policy = new BlockPolicy( - blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000) + blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000), + getValidatorSet: idx => TestUtils.ValidatorSet ); var stateStore = new TrieStateStore(stateKeyValueStore); IStore store = new MemoryStore(); @@ -195,7 +196,8 @@ public void ValidateNextBlockInvalidStateRootHash() var policyWithBlockAction = new BlockPolicy( new SetStatesAtBlock(default, (Text)"foo", 1), - policy.BlockInterval + policy.BlockInterval, + getValidatorSet: idx => TestUtils.ValidatorSet ); var chain2 = new BlockChain( policyWithBlockAction, diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.cs b/Libplanet.Tests/Blockchain/BlockChainTest.cs index 97abcc45af..8fc489afdd 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.cs @@ -310,7 +310,8 @@ public void ShortCircuitActionEvaluationForUnrenderWithNoActionRenderers() IEnumerable NonRehearsalExecutions() => DumbAction.ExecuteRecords.Value.Where(r => !r.Rehearsal); - var policy = new BlockPolicy(); + var policy = new BlockPolicy( + getValidatorSet: idx => ValidatorSet); var key = new PrivateKey(); Address miner = key.ToAddress(); @@ -363,7 +364,8 @@ IEnumerable NonRehearsalExecutions() => [Fact] public void ActionRenderersHaveDistinctContexts() { - var policy = new NullBlockPolicy(); + var policy = new NullBlockPolicy( + getValidatorSet: idx => ValidatorSet); var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var generatedRandomValueLogs = new List(); @@ -401,7 +403,8 @@ public void ActionRenderersHaveDistinctContexts() [Fact] public void RenderActionsAfterBlockIsRendered() { - var policy = new NullBlockPolicy(); + var policy = new NullBlockPolicy( + getValidatorSet: idx => ValidatorSet); var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var recordingRenderer = new RecordingActionRenderer(); @@ -439,7 +442,8 @@ public void RenderActionsAfterBlockIsRendered() [Fact] public void RenderActionsAfterAppendComplete() { - var policy = new NullBlockPolicy(); + var policy = new NullBlockPolicy( + getValidatorSet: idx => ValidatorSet); var store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); IActionRenderer renderer = new AnonymousActionRenderer @@ -1332,7 +1336,8 @@ public void GetStateReturnsValidStateAfterFork() var chain = new BlockChain( - new NullBlockPolicy(), + new NullBlockPolicy( + getValidatorSet: idx => ValidatorSet), new VolatileStagePolicy(), store, stateStore, diff --git a/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs b/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs index 0fbec34565..47b45cc214 100644 --- a/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs +++ b/Libplanet.Tests/Blocks/PreEvaluationBlockTest.cs @@ -29,7 +29,8 @@ public void Evaluate() var blockAction = new SetStatesAtBlock(address, (Bencodex.Types.Integer)123, 0); var policy = new BlockPolicy( blockAction: blockAction, - blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000)); + blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000), + getValidatorSet: idx => ValidatorSet); var stagePolicy = new VolatileStagePolicy(); PreEvaluationBlock preEvalGenesis = @@ -91,7 +92,8 @@ public void DetermineStateRootHash() var blockAction = new SetStatesAtBlock(address, (Bencodex.Types.Integer)123, 0); var policy = new BlockPolicy( blockAction: blockAction, - blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000)); + blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000), + getValidatorSet: idx => ValidatorSet); var stagePolicy = new VolatileStagePolicy(); PreEvaluationBlock preEvalGenesis = From abdf4e7a88c80088ad5ee98de80cc2d10e3b9719 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Fri, 18 Nov 2022 18:19:50 +0900 Subject: [PATCH 4/8] test/explorer: fix tests --- .../Queries/TransactionQueryTest.cs | 17 ++++++----------- Libplanet.Net.Tests/SwarmTest.cs | 7 +++++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs index 74a8e6a0f1..8971374cab 100644 --- a/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs +++ b/Libplanet.Explorer.Tests/Queries/TransactionQueryTest.cs @@ -123,14 +123,9 @@ async Task AssertNextNonce(long expected, Address address) { // staging txs of key2 does not increase nonce of key1 _source.BlockChain.MakeTransaction(key2, ImmutableList.Empty.Add(new NullAction())); - var lastCommit = new BlockCommit( - height: 1, - round: 0, - blockHash: block.Hash, - votes: ImmutableArray.Empty - .Add(new VoteMetadata(1, 0, block.Hash, DateTimeOffset.UtcNow, - _source.Validator.PublicKey, VoteFlag.PreCommit).Sign(_source.Validator))); - block = _source.BlockChain.ProposeBlock(new PrivateKey(), lastCommit: lastCommit); + block = _source.BlockChain.ProposeBlock( + new PrivateKey(), + lastCommit: Libplanet.Tests.TestUtils.CreateBlockCommit(block)); _source.BlockChain.Append(block, Libplanet.Tests.TestUtils.CreateBlockCommit(block)); await AssertNextNonce(1, key2.ToAddress()); await AssertNextNonce(2, key1.ToAddress()); @@ -157,7 +152,7 @@ private class MockBlockChainContext : IBlockChainContext public MockBlockChainContext() { - Validator = new PrivateKey(); + Validator = Libplanet.Tests.TestUtils.ValidatorPrivateKeys[1]; Store = new MemoryStore(); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var minerKey = new PrivateKey(); @@ -175,8 +170,8 @@ public MockBlockChainContext() _ => true, stateStore); BlockChain = new BlockChain( - new BlockPolicy(getValidatorSet: index => new ValidatorSet( - new List { Validator.PublicKey })), + new BlockPolicy( + getValidatorSet: index => Libplanet.Tests.TestUtils.ValidatorSet), new VolatileStagePolicy(), Store, stateStore, diff --git a/Libplanet.Net.Tests/SwarmTest.cs b/Libplanet.Net.Tests/SwarmTest.cs index 8dd249ebe4..6318124204 100644 --- a/Libplanet.Net.Tests/SwarmTest.cs +++ b/Libplanet.Net.Tests/SwarmTest.cs @@ -470,7 +470,9 @@ public async Task GetTx() { var keyB = new PrivateKey(); - var policy = new BlockPolicy(new MinerReward(1)); + var policy = new BlockPolicy( + new MinerReward(1), + getValidatorSet: idx => ValidatorSet); Block genesis = BlockChain.ProposeGenesisBlock( privateKey: new PrivateKey(), blockAction: policy.BlockAction); Swarm swarmA = CreateSwarm(genesis: genesis, policy: policy); @@ -1222,7 +1224,8 @@ public async Task DoNotReceiveBlockFromNodeHavingDifferenceGenesisBlock() BlockChain MakeGenesisChain( IStore store, IStateStore stateStore, Block genesisBlock) => new BlockChain( - new BlockPolicy(), + new BlockPolicy( + getValidatorSet: idx => ValidatorSet), new VolatileStagePolicy(), store, stateStore, From 24f39e2ecb625125bd9c872ee19ea26b8cc83a68 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Fri, 18 Nov 2022 18:20:32 +0900 Subject: [PATCH 5/8] test/reorg: skip reorg test --- Libplanet.Net.Tests/SwarmTest.Preload.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libplanet.Net.Tests/SwarmTest.Preload.cs b/Libplanet.Net.Tests/SwarmTest.Preload.cs index 0761f5b9ce..17eabf1609 100644 --- a/Libplanet.Net.Tests/SwarmTest.Preload.cs +++ b/Libplanet.Net.Tests/SwarmTest.Preload.cs @@ -703,7 +703,7 @@ public async Task GetDemandBlockHashes() Assert.Equal(expectedBlocks, demands); } - [Fact(Timeout = Timeout)] + [Fact(Timeout = Timeout, Skip = "No Reorganization in PBFT")] public async Task PreloadAfterReorg() { var minerKey = new PrivateKey(); From b530dea2749f8627804064c6831bde6b9a5bcae4 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Fri, 18 Nov 2022 19:05:21 +0900 Subject: [PATCH 6/8] chore/changelog: missing `InvalidBlockCommitException` --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 5859c60de2..d96ee11192 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -59,6 +59,7 @@ Version PBFT - Added `BlockMarshaler.UnmarshalBlockHash()` method. [[#PBFT]] - Added `BlockChain.GetBlockCommit()` method. [[#PBFT]] - Added `BlockChain.CleanupBlockCommitStore()` method. [[#PBFT]] + - Added `InvalidBlockCommitException` class. [[#PBFT]] - Added `BlockChain.ValidateBlockCommit()` method [[#PBFT]] - (Libplanet.Net) Added `IReactor` interface. [[#PBFT]] - (Libplanet.Net) Added `ConsensusReactor` class which inherits From 4d4558fed43ad72be0a4e9701cca7b7766ff45af Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Fri, 18 Nov 2022 22:18:10 +0900 Subject: [PATCH 7/8] fix: change protection level to internal --- Libplanet/Blockchain/BlockChain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libplanet/Blockchain/BlockChain.cs b/Libplanet/Blockchain/BlockChain.cs index e22d48fb37..01d2cc3058 100644 --- a/Libplanet/Blockchain/BlockChain.cs +++ b/Libplanet/Blockchain/BlockChain.cs @@ -1578,7 +1578,7 @@ internal void CleanupBlockCommitStore(long except, long maxCacheSize = 30) } #pragma warning disable SA1202 - public InvalidBlockCommitException ValidateBlockCommit( + internal InvalidBlockCommitException ValidateBlockCommit( Block block, BlockCommit blockCommit) #pragma warning restore SA1202 From ec467649912071dd7ba0452908ea8f99231d3400 Mon Sep 17 00:00:00 2001 From: Colibri Shin Date: Sat, 19 Nov 2022 02:53:51 +0900 Subject: [PATCH 8/8] test: regression --- .../BlockChainTest.ValidateNextBlock.cs | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs index f86170daaa..2c540907fb 100644 --- a/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs +++ b/Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs @@ -321,5 +321,118 @@ public void ValidateNextBlockLastCommitFailsDropExpectedValidator() Assert.Throws(() => _blockChain.Append(block2, TestUtils.CreateBlockCommit(block2))); } + + [Fact] + public void ValidateBlockCommitGenesis() + { + InvalidBlockCommitException ibcm = + _blockChain.ValidateBlockCommit(_fx.GenesisBlock, null); + + Assert.Null(ibcm); + + ibcm = _blockChain.ValidateBlockCommit( + _fx.GenesisBlock, + new BlockCommit( + 0, + 0, + _fx.GenesisBlock.Hash, + TestUtils.ValidatorPrivateKeys.Select(x => new VoteMetadata( + 0, + 0, + _fx.GenesisBlock.Hash, + DateTimeOffset.UtcNow, + x.PublicKey, + VoteFlag.PreCommit).Sign(x)).ToImmutableArray())); + + Assert.NotNull(ibcm); + } + + [Fact] + public void ValidateBlockCommitFailsDifferentBlockHash() + { + Block validNextBlock = new BlockContent( + new BlockMetadata( + index: 1L, + timestamp: _fx.GenesisBlock.Timestamp.AddDays(1), + publicKey: _fx.Miner.PublicKey, + previousHash: _fx.GenesisBlock.Hash, + txHash: null, + lastCommit: null)).Propose().Evaluate(_fx.Miner, _blockChain); + + Assert.Throws(() => + _blockChain.Append( + validNextBlock, + TestUtils.CreateBlockCommit( + new BlockHash(TestUtils.GetRandomBytes(BlockHash.Size)), + 1, + 0))); + } + + [Fact] + public void ValidateBlockCommitFailsDifferentHeight() + { + Block validNextBlock = new BlockContent( + new BlockMetadata( + index: 1L, + timestamp: _fx.GenesisBlock.Timestamp.AddDays(1), + publicKey: _fx.Miner.PublicKey, + previousHash: _fx.GenesisBlock.Hash, + txHash: null, + lastCommit: null)).Propose().Evaluate(_fx.Miner, _blockChain); + + Assert.Throws(() => + _blockChain.Append( + validNextBlock, + TestUtils.CreateBlockCommit( + validNextBlock.Hash, + 2, + 0))); + } + + [Fact] + public void ValidateBlockCommitFailsDifferentValidatorSet() + { + Block validNextBlock = new BlockContent( + new BlockMetadata( + index: 1L, + timestamp: _fx.GenesisBlock.Timestamp.AddDays(1), + publicKey: _fx.Miner.PublicKey, + previousHash: _fx.GenesisBlock.Hash, + txHash: null, + lastCommit: null)).Propose().Evaluate(_fx.Miner, _blockChain); + + Assert.Throws(() => + _blockChain.Append( + validNextBlock, + new BlockCommit( + 1, + 0, + validNextBlock.Hash, + Enumerable.Range(0, TestUtils.ValidatorSet.TotalCount) + .Select(x => new PrivateKey()) + .Select(x => new VoteMetadata( + 1, + 0, + validNextBlock.Hash, + DateTimeOffset.UtcNow, + x.PublicKey, + VoteFlag.PreCommit).Sign(x)).ToImmutableArray()))); + } + + [Fact] + public void ValidateBlockCommitFailsNullBlockCommit() + { + Block validNextBlock = new BlockContent( + new BlockMetadata( + index: 1L, + timestamp: _fx.GenesisBlock.Timestamp.AddDays(1), + publicKey: _fx.Miner.PublicKey, + previousHash: _fx.GenesisBlock.Hash, + txHash: null, + lastCommit: null)).Propose().Evaluate(_fx.Miner, _blockChain); + + Assert.Throws(() => + _blockChain.Append(validNextBlock, null)); + } } }