diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index ad7f9d8b4e9..64dfba9622c 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; import org.hyperledger.besu.ethereum.chain.GenesisState; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Synchronizer; @@ -371,7 +372,8 @@ public BesuController build() { syncState, dataDirectory, clock, - metricsSystem); + metricsSystem, + getTerminalTotalDifficulty()); final MiningCoordinator miningCoordinator = createMiningCoordinator( @@ -416,6 +418,10 @@ public BesuController build() { additionalPluginServices); } + protected Optional getTerminalTotalDifficulty() { + return genesisConfig.getConfigOptions().getTerminalTotalDifficulty().map(Difficulty::of); + } + protected void prepForBuild() {} protected JsonRpcMethods createAdditionalJsonRpcMethodFactory( diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java index 59440e65d4c..3d735574d9e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastDownloaderFactory; @@ -66,7 +67,8 @@ public DefaultSynchronizer( final SyncState syncState, final Path dataDirectory, final Clock clock, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final Optional terminalTotalDifficulty) { this.maybePruner = maybePruner; this.syncState = syncState; @@ -91,7 +93,13 @@ public DefaultSynchronizer( this.fullSyncDownloader = new FullSyncDownloader( - syncConfig, protocolSchedule, protocolContext, ethContext, syncState, metricsSystem); + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + syncState, + metricsSystem, + terminalTotalDifficulty); this.fastSyncDownloader = FastDownloaderFactory.create( syncConfig, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java index 9ae54249a11..08e089e86bb 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fullsync; import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.PipelineChainDownloader; @@ -23,6 +24,8 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.plugin.services.MetricsSystem; +import java.util.Optional; + public class FullSyncChainDownloader { private FullSyncChainDownloader() {} @@ -32,11 +35,17 @@ public static ChainDownloader create( final ProtocolContext protocolContext, final EthContext ethContext, final SyncState syncState, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final Optional terminalTotalDifficulty) { final FullSyncTargetManager syncTargetManager = new FullSyncTargetManager( - config, protocolSchedule, protocolContext, ethContext, metricsSystem); + config, + protocolSchedule, + protocolContext, + ethContext, + metricsSystem, + terminalTotalDifficulty); return new PipelineChainDownloader( syncState, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java index cdcbf861aec..0e3a39719df 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fullsync; import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -23,6 +24,8 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.plugin.services.MetricsSystem; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,14 +43,21 @@ public FullSyncDownloader( final ProtocolContext protocolContext, final EthContext ethContext, final SyncState syncState, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final Optional terminalTotalDifficulty) { this.syncConfig = syncConfig; this.protocolContext = protocolContext; this.syncState = syncState; this.chainDownloader = FullSyncChainDownloader.create( - syncConfig, protocolSchedule, protocolContext, ethContext, syncState, metricsSystem); + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + syncState, + metricsSystem, + terminalTotalDifficulty); } public void start() { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java index 404a595de8f..1671484f113 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.sync.SyncTargetManager; @@ -39,16 +40,19 @@ class FullSyncTargetManager extends SyncTargetManager { private static final Logger LOG = LoggerFactory.getLogger(FullSyncTargetManager.class); private final ProtocolContext protocolContext; private final EthContext ethContext; + private final Optional terminalTotalDifficulty; FullSyncTargetManager( final SynchronizerConfiguration config, final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final Optional terminalTotalDifficulty) { super(config, protocolSchedule, protocolContext, ethContext, metricsSystem); this.protocolContext = protocolContext; this.ethContext = ethContext; + this.terminalTotalDifficulty = terminalTotalDifficulty; } @Override @@ -105,6 +109,9 @@ private boolean isSyncTargetReached(final EthPeer peer) { @Override public boolean shouldContinueDownloading() { - return true; + return terminalTotalDifficulty.isEmpty() + || terminalTotalDifficulty + .get() + .greaterThan(protocolContext.getBlockchain().getChainHead().getTotalDifficulty()); } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java index b87bea35690..0a1cb1c125f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java @@ -35,6 +35,8 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; +import java.util.Optional; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -80,7 +82,13 @@ public void tearDown() { private ChainDownloader downloader(final SynchronizerConfiguration syncConfig) { return FullSyncChainDownloader.create( - syncConfig, protocolSchedule, protocolContext, ethContext, syncState, metricsSystem); + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + syncState, + metricsSystem, + Optional.empty()); } private ChainDownloader downloader() { diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java index 0fd530673ac..64d52989323 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java @@ -117,7 +117,13 @@ public void tearDown() { private ChainDownloader downloader(final SynchronizerConfiguration syncConfig) { return FullSyncChainDownloader.create( - syncConfig, protocolSchedule, protocolContext, ethContext, syncState, metricsSystem); + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + syncState, + metricsSystem, + Optional.empty()); } private ChainDownloader downloader() { diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java new file mode 100644 index 00000000000..7168cb382e9 --- /dev/null +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java @@ -0,0 +1,181 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.eth.sync.fullsync; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; +import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; +import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; +import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; +import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; +import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; +import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.services.MetricsSystem; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FullSyncChainDownloaderTotalTerminalDifficultyTest { + + protected ProtocolSchedule protocolSchedule; + protected EthProtocolManager ethProtocolManager; + protected EthContext ethContext; + protected ProtocolContext protocolContext; + private SyncState syncState; + + private BlockchainSetupUtil localBlockchainSetup; + protected MutableBlockchain localBlockchain; + private BlockchainSetupUtil otherBlockchainSetup; + protected Blockchain otherBlockchain; + private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); + private static final Difficulty TARGET_TERMINAL_DIFFICULTY = Difficulty.of(1_000_000L); + + @Parameters + public static Collection data() { + return Arrays.asList(new Object[][] {{DataStorageFormat.BONSAI}, {DataStorageFormat.FOREST}}); + } + + private final DataStorageFormat storageFormat; + + public FullSyncChainDownloaderTotalTerminalDifficultyTest(final DataStorageFormat storageFormat) { + this.storageFormat = storageFormat; + } + + @Before + public void setupTest() { + localBlockchainSetup = BlockchainSetupUtil.forTesting(storageFormat); + localBlockchain = localBlockchainSetup.getBlockchain(); + otherBlockchainSetup = BlockchainSetupUtil.forTesting(storageFormat); + otherBlockchain = otherBlockchainSetup.getBlockchain(); + + protocolSchedule = localBlockchainSetup.getProtocolSchedule(); + protocolContext = localBlockchainSetup.getProtocolContext(); + ethProtocolManager = + EthProtocolManagerTestUtil.create( + localBlockchain, + new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()), + localBlockchainSetup.getWorldArchive(), + localBlockchainSetup.getTransactionPool(), + EthProtocolConfiguration.defaultConfig()); + ethContext = ethProtocolManager.ethContext(); + syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); + } + + @After + public void tearDown() { + ethProtocolManager.stop(); + } + + private ChainDownloader downloader( + final SynchronizerConfiguration syncConfig, + final Optional targetTerminalDifficulty1) { + return FullSyncChainDownloader.create( + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + syncState, + metricsSystem, + targetTerminalDifficulty1); + } + + private SynchronizerConfiguration.Builder syncConfigBuilder() { + return SynchronizerConfiguration.builder(); + } + + @Test + public void syncsFullyAndStopsWhenTTDReached() { + otherBlockchainSetup.importFirstBlocks(30); + final long targetBlock = otherBlockchain.getChainHeadBlockNumber(); + // Sanity check + assertThat(targetBlock).isGreaterThan(localBlockchain.getChainHeadBlockNumber()); + + final RespondingEthPeer peer = + EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain); + final RespondingEthPeer.Responder responder = + RespondingEthPeer.blockchainResponder(otherBlockchain); + + final SynchronizerConfiguration syncConfig = + syncConfigBuilder().downloaderChainSegmentSize(1).downloaderParallelism(1).build(); + final ChainDownloader downloader = + downloader(syncConfig, Optional.of(TARGET_TERMINAL_DIFFICULTY)); + final CompletableFuture future = downloader.start(); + + assertThat(future.isDone()).isFalse(); + + peer.respondWhileOtherThreadsWork(responder, () -> !syncState.syncTarget().isPresent()); + assertThat(syncState.syncTarget()).isPresent(); + assertThat(syncState.syncTarget().get().peer()).isEqualTo(peer.getEthPeer()); + + peer.respondWhileOtherThreadsWork( + responder, () -> localBlockchain.getChainHeadBlockNumber() < targetBlock); + + assertThat(localBlockchain.getChainHeadBlockNumber()).isEqualTo(targetBlock); + + assertThat(future.isDone()).isTrue(); + } + + @Test + public void syncsFullyAndContinuesWhenTTDNotSpecified() { + otherBlockchainSetup.importFirstBlocks(30); + final long targetBlock = otherBlockchain.getChainHeadBlockNumber(); + // Sanity check + assertThat(targetBlock).isGreaterThan(localBlockchain.getChainHeadBlockNumber()); + + final RespondingEthPeer peer = + EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain); + final RespondingEthPeer.Responder responder = + RespondingEthPeer.blockchainResponder(otherBlockchain); + + final SynchronizerConfiguration syncConfig = + syncConfigBuilder().downloaderChainSegmentSize(1).downloaderParallelism(1).build(); + final ChainDownloader downloader = downloader(syncConfig, Optional.empty()); + final CompletableFuture future = downloader.start(); + + assertThat(future.isDone()).isFalse(); + + peer.respondWhileOtherThreadsWork(responder, () -> !syncState.syncTarget().isPresent()); + assertThat(syncState.syncTarget()).isPresent(); + assertThat(syncState.syncTarget().get().peer()).isEqualTo(peer.getEthPeer()); + + peer.respondWhileOtherThreadsWork( + responder, () -> localBlockchain.getChainHeadBlockNumber() < targetBlock); + + assertThat(localBlockchain.getChainHeadBlockNumber()).isEqualTo(targetBlock); + + assertThat(future.isDone()).isFalse(); + } +} diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java index bab67345bd3..494abd37603 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java @@ -35,6 +35,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Optional; import org.junit.After; import org.junit.Before; @@ -92,7 +93,13 @@ public void tearDown() { private FullSyncDownloader downloader(final SynchronizerConfiguration syncConfig) { return new FullSyncDownloader( - syncConfig, protocolSchedule, protocolContext, ethContext, syncState, metricsSystem); + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + syncState, + metricsSystem, + Optional.empty()); } @Test diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java index e99fff3a1f9..12324396b26 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java @@ -40,6 +40,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.junit.After; @@ -97,7 +98,8 @@ public void setup() { protocolSchedule, protocolContext, ethContext, - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + Optional.empty()); } @After