diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs index 1993a8fce0f..c38d5c264f2 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs @@ -3,11 +3,13 @@ using Nethermind.Consensus.Validators; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Logging; using Nethermind.Specs; using Nethermind.Specs.Test; +using NSubstitute; using NUnit.Framework; namespace Nethermind.Blockchain.Test.Validators @@ -30,5 +32,61 @@ public void When_more_uncles_than_allowed_returns_false() bool result = blockValidator.ValidateSuggestedBlock(Build.A.Block.WithUncles(Build.A.BlockHeader.TestObject).TestObject); Assert.False(result); } + + [Test] + public void ValidateBodyAgainstHeader_BlockIsValid_ReturnsTrue() + { + Block block = Build.A.Block + .WithTransactions(1, Substitute.For()) + .WithWithdrawals(1) + .TestObject; + + Assert.That( + BlockValidator.ValidateBodyAgainstHeader(block.Header, block.Body), + Is.True); + } + + [Test] + public void ValidateBodyAgainstHeader_BlockHasInvalidTxRoot_ReturnsFalse() + { + Block block = Build.A.Block + .WithTransactions(1, Substitute.For()) + .WithWithdrawals(1) + .TestObject; + block.Header.TxRoot = Keccak.OfAnEmptyString; + + Assert.That( + BlockValidator.ValidateBodyAgainstHeader(block.Header, block.Body), + Is.False); + } + + + [Test] + public void ValidateBodyAgainstHeader_BlockHasInvalidUnclesRoot_ReturnsFalse() + { + Block block = Build.A.Block + .WithTransactions(1, Substitute.For()) + .WithWithdrawals(1) + .TestObject; + block.Header.UnclesHash = Keccak.OfAnEmptyString; + + Assert.That( + BlockValidator.ValidateBodyAgainstHeader(block.Header, block.Body), + Is.False); + } + + [Test] + public void ValidateBodyAgainstHeader_BlockHasInvalidWithdrawalsRoot_ReturnsFalse() + { + Block block = Build.A.Block + .WithTransactions(1, Substitute.For()) + .WithWithdrawals(1) + .TestObject; + block.Header.WithdrawalsRoot = Keccak.OfAnEmptyString; + + Assert.That( + BlockValidator.ValidateBodyAgainstHeader(block.Header, block.Body), + Is.False); + } } } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 971a1c42a72..db786c1b547 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -306,28 +306,47 @@ private bool ValidateEip4844Fields(Block block, IReleaseSpec spec, out string? e return true; } + public static bool ValidateBodyAgainstHeader(BlockHeader header, BlockBody toBeValidated) => + ValidateTxRootMatchesTxs(header, toBeValidated, out _) && + ValidateUnclesHashMatches(header, toBeValidated, out _) && + ValidateWithdrawalsHashMatches(header, toBeValidated, out _); + public static bool ValidateTxRootMatchesTxs(Block block, out Hash256 txRoot) { - txRoot = new TxTrie(block.Transactions).RootHash; - return txRoot == block.Header.TxRoot; + return ValidateTxRootMatchesTxs(block.Header, block.Body, out txRoot); + } + public static bool ValidateTxRootMatchesTxs(BlockHeader header, BlockBody body, out Hash256 txRoot) + { + txRoot = new TxTrie(body.Transactions).RootHash; + return txRoot == header.TxRoot; } public static bool ValidateUnclesHashMatches(Block block, out Hash256 unclesHash) { - unclesHash = UnclesHash.Calculate(block); + return ValidateUnclesHashMatches(block.Header, block.Body, out unclesHash); + } - return block.Header.UnclesHash == unclesHash; + public static bool ValidateUnclesHashMatches(BlockHeader header, BlockBody body, out Hash256 unclesHash) + { + unclesHash = UnclesHash.Calculate(body.Uncles); + + return header.UnclesHash == unclesHash; } public static bool ValidateWithdrawalsHashMatches(Block block, out Hash256? withdrawalsRoot) + { + return ValidateWithdrawalsHashMatches(block.Header, block.Body, out withdrawalsRoot); + } + + public static bool ValidateWithdrawalsHashMatches(BlockHeader header, BlockBody body, out Hash256? withdrawalsRoot) { withdrawalsRoot = null; - if (block.Withdrawals == null) - return block.Header.WithdrawalsRoot == null; + if (body.Withdrawals == null) + return header.WithdrawalsRoot == null; - withdrawalsRoot = new WithdrawalTrie(block.Withdrawals).RootHash; + withdrawalsRoot = new WithdrawalTrie(body.Withdrawals).RootHash; - return block.Header.WithdrawalsRoot == withdrawalsRoot; + return header.WithdrawalsRoot == withdrawalsRoot; } private static string Invalid(Block block) => diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs index 62803ba7f57..d2809c473e0 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Nethermind.Blockchain; using Nethermind.Blockchain.Synchronization; +using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; @@ -193,10 +194,7 @@ public override SyncResponseHandlingResult HandleResponse(BodiesSyncBatch? batch private bool TryPrepareBlock(BlockInfo blockInfo, BlockBody blockBody, out Block? block) { BlockHeader header = _blockTree.FindHeader(blockInfo.BlockHash, blockNumber: blockInfo.BlockNumber); - Hash256 rootHash = TxTrie.CalculateRoot(blockBody.Transactions); - bool txRootIsValid = rootHash == header.TxRoot; - bool unclesHashIsValid = UnclesHash.Calculate(blockBody.Uncles) == header.UnclesHash; - if (txRootIsValid && unclesHashIsValid) + if (BlockValidator.ValidateBodyAgainstHeader(header, blockBody)) { block = new Block(header, blockBody); }