mirror of https://github.com/hyperledger/besu
FullSync Future should stop when total terminal difficulty is reached (#3423)
* FullSync Future should stop when total terminal difficulty is reached Signed-off-by: Jiri Peinlich <jiri.peinlich@gmail.com>pull/3516/head
parent
b73f5f98ad
commit
605ff69d3f
@ -0,0 +1,37 @@ |
||||
/* |
||||
* 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 org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
|
||||
public class FlexibleBlockHashTerminalCondition implements SyncTerminationCondition { |
||||
private Hash blockHash; |
||||
private final Blockchain blockchain; |
||||
|
||||
public FlexibleBlockHashTerminalCondition(final Hash blockHash, final Blockchain blockchain) { |
||||
this.blockHash = blockHash; |
||||
this.blockchain = blockchain; |
||||
} |
||||
|
||||
public synchronized void setBlockHash(final Hash blockHash) { |
||||
this.blockHash = blockHash; |
||||
} |
||||
|
||||
@Override |
||||
public synchronized boolean getAsBoolean() { |
||||
return blockchain.contains(blockHash); |
||||
} |
||||
} |
@ -0,0 +1,83 @@ |
||||
/* |
||||
* 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 org.hyperledger.besu.datatypes.Hash; |
||||
import org.hyperledger.besu.ethereum.chain.Blockchain; |
||||
import org.hyperledger.besu.ethereum.core.Difficulty; |
||||
|
||||
import java.util.function.BooleanSupplier; |
||||
|
||||
import org.apache.tuweni.units.bigints.UInt256; |
||||
|
||||
/** return true when termination condition is fullfilled and the full sync should stop */ |
||||
public interface SyncTerminationCondition extends BooleanSupplier { |
||||
|
||||
default boolean shouldContinueDownload() { |
||||
return !shouldStopDownload(); |
||||
} |
||||
|
||||
default boolean shouldStopDownload() { |
||||
return getAsBoolean(); |
||||
} |
||||
|
||||
/** |
||||
* When we want full sync to continue forever (for instance when we don't want to merge) |
||||
* |
||||
* @return always false therefore continues forever * |
||||
*/ |
||||
static SyncTerminationCondition never() { |
||||
return () -> false; |
||||
} |
||||
|
||||
/** |
||||
* When we want full sync to finish after reaching a difficulty. For instance when we merge on |
||||
* total terminal difficulty. |
||||
* |
||||
* @param targetDifficulty target difficulty to reach |
||||
* @param blockchain blockchain to reach the difficulty on |
||||
* @return true when blockchain reaches difficulty |
||||
*/ |
||||
static SyncTerminationCondition difficulty( |
||||
final UInt256 targetDifficulty, final Blockchain blockchain) { |
||||
return difficulty(Difficulty.of(targetDifficulty), blockchain); |
||||
} |
||||
|
||||
/** |
||||
* When we want full sync to finish after reaching a difficulty. For instance when we merge on |
||||
* total terminal difficulty. |
||||
* |
||||
* @param targetDifficulty target difficulty to reach |
||||
* @param blockchain blockchain to reach the difficulty on* |
||||
* @return true when blockchain reaches difficulty |
||||
*/ |
||||
static SyncTerminationCondition difficulty( |
||||
final Difficulty targetDifficulty, final Blockchain blockchain) { |
||||
return () -> blockchain.getChainHead().getTotalDifficulty().greaterThan(targetDifficulty); |
||||
} |
||||
|
||||
/** |
||||
* When we want the full sync to finish on a target hash. For instance when we reach a merge |
||||
* checkpoint. |
||||
* |
||||
* @param blockHash target hash to look for |
||||
* @param blockchain blockchain to reach the difficulty on |
||||
* @return true when blockchain contains target hash (target hash can be changed) |
||||
*/ |
||||
static FlexibleBlockHashTerminalCondition blockHash( |
||||
final Hash blockHash, final Blockchain blockchain) { |
||||
return new FlexibleBlockHashTerminalCondition(blockHash, blockchain); |
||||
} |
||||
} |
@ -0,0 +1,182 @@ |
||||
/* |
||||
* 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.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<Object[]> 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 SyncTerminationCondition terminalCondition) { |
||||
return FullSyncChainDownloader.create( |
||||
syncConfig, |
||||
protocolSchedule, |
||||
protocolContext, |
||||
ethContext, |
||||
syncState, |
||||
metricsSystem, |
||||
terminalCondition); |
||||
} |
||||
|
||||
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, |
||||
SyncTerminationCondition.difficulty(TARGET_TERMINAL_DIFFICULTY, localBlockchain)); |
||||
final CompletableFuture<Void> future = downloader.start(); |
||||
|
||||
assertThat(future.isDone()).isFalse(); |
||||
|
||||
peer.respondWhileOtherThreadsWork(responder, () -> syncState.syncTarget().isEmpty()); |
||||
assertThat(syncState.syncTarget()).isPresent(); |
||||
assertThat(syncState.syncTarget().get().peer()).isEqualTo(peer.getEthPeer()); |
||||
|
||||
peer.respondWhileOtherThreadsWork(responder, () -> !future.isDone()); |
||||
|
||||
assertThat(localBlockchain.getChainHead().getTotalDifficulty()) |
||||
.isGreaterThan(TARGET_TERMINAL_DIFFICULTY); |
||||
|
||||
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, SyncTerminationCondition.never()); |
||||
final CompletableFuture<Void> 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(); |
||||
} |
||||
} |
Loading…
Reference in new issue