From edf58a54a34211b718db538ed267e1bdc44f7470 Mon Sep 17 00:00:00 2001 From: garyschulte Date: Tue, 12 Mar 2024 18:19:37 -0700 Subject: [PATCH] Support pruned chain history in peer validators (#6698) * dao fork block presence is optional in peer validator to support peers with pruned chain history Signed-off-by: garyschulte * Update ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java Co-authored-by: Sally MacFarlane Signed-off-by: garyschulte --------- Signed-off-by: garyschulte Co-authored-by: Sally MacFarlane Signed-off-by: amsmota --- .../AbstractPeerBlockValidator.java | 24 ++++++++++++++----- .../peervalidation/DaoForkPeerValidator.java | 9 +++++++ .../DaoForkPeerValidatorTest.java | 24 +++++++++++++++++++ .../RequiredBlocksPeerValidatorTest.java | 22 +++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java index 55c3dd89e56..4483e14cdfa 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java @@ -86,12 +86,20 @@ public CompletableFuture validatePeer( } final List headers = res.getResult(); if (headers.size() == 0) { - // If no headers are returned, fail - LOG.debug( - "Peer {} is invalid because required block ({}) is unavailable.", - ethPeer, - blockNumber); - return false; + if (blockIsRequired()) { + // If no headers are returned, fail + LOG.debug( + "Peer {} is invalid because required block ({}) is unavailable.", + ethPeer, + blockNumber); + return false; + } else { + LOG.debug( + "Peer {} deemed valid because unavailable block ({}) is not required.", + ethPeer, + blockNumber); + return true; + } } final BlockHeader header = headers.get(0); return validateBlockHeader(ethPeer, header); @@ -105,6 +113,10 @@ public boolean canBeValidated(final EthPeer ethPeer) { return ethPeer.chainState().getEstimatedHeight() >= (blockNumber + chainHeightEstimationBuffer); } + protected boolean blockIsRequired() { + return true; + } + @Override public Duration nextValidationCheckTimeout(final EthPeer ethPeer) { if (!ethPeer.chainState().hasEstimatedHeight()) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java index d038ad0e9a9..01ce6144ee0 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java @@ -49,4 +49,13 @@ boolean validateBlockHeader(final EthPeer ethPeer, final BlockHeader header) { } return validDaoBlock; } + + /** + * In order to support chain history pruning, clients do not need to have the dao fork block to be + * deemed valid. + */ + @Override + protected boolean blockIsRequired() { + return false; + } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java index 8586874c434..042ca8f9605 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java @@ -98,4 +98,28 @@ public void validatePeer_responsivePeerOnWrongSideOfFork() { assertThat(result).isDone(); assertThat(result).isCompletedWithValue(false); } + + @Test + public void validatePeer_responsivePeerDoesNotHaveBlockWhenPastForkHeight() { + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + final long daoBlockNumber = 500; + + final PeerValidator validator = + new DaoForkPeerValidator( + ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), daoBlockNumber, 0); + + final RespondingEthPeer peer = + EthProtocolManagerTestUtil.createPeer(ethProtocolManager, daoBlockNumber); + + final CompletableFuture result = + validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); + + assertThat(result).isNotDone(); + + // Respond to block header request with empty + peer.respond(RespondingEthPeer.emptyResponder()); + + assertThat(result).isDone(); + assertThat(result).isCompletedWithValue(true); + } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java index 67c80345880..87b3c7077cc 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java @@ -104,4 +104,26 @@ public void validatePeer_responsivePeerWithBadRequiredBlock() { assertThat(result).isDone(); assertThat(result).isCompletedWithValue(false); } + + @Test + public void validatePeer_responsivePeerDoesNotHaveBlockWhenPastForkHeight() { + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + + final PeerValidator validator = + new RequiredBlocksPeerValidator( + ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), 1, Hash.ZERO); + + final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1); + + final CompletableFuture result = + validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); + + assertThat(result).isNotDone(); + + // Respond to block header request with empty + peer.respond(RespondingEthPeer.emptyResponder()); + + assertThat(result).isDone(); + assertThat(result).isCompletedWithValue(false); + } }