From 8589e56d7d844105a2f79a2818caabfa04861e90 Mon Sep 17 00:00:00 2001 From: garyschulte Date: Thu, 7 Mar 2024 13:29:41 -0800 Subject: [PATCH] dao fork block presence is optional in peer validator to support peers with pruned chain history Signed-off-by: garyschulte --- .../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..656059356ef 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 despite block ({}) being unavailable.", + 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); + } }