mirror of https://github.com/hyperledger/besu
7536 use head for snap sync (#7718)
* Add support to sync to head instead of safe block during snapsync Signed-off-by: Kevin King <kevin.king@consensys.net> Signed-off-by: kingnhcomcast <114761064+kingnhcomcast@users.noreply.github.com>pull/7740/head
parent
21e95a9efb
commit
a7e1f6ace0
@ -0,0 +1,167 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* 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.fastsync; |
||||||
|
|
||||||
|
import org.hyperledger.besu.config.GenesisConfigOptions; |
||||||
|
import org.hyperledger.besu.consensus.merge.ForkchoiceEvent; |
||||||
|
import org.hyperledger.besu.datatypes.Hash; |
||||||
|
import org.hyperledger.besu.ethereum.ProtocolContext; |
||||||
|
import org.hyperledger.besu.ethereum.core.BlockHeader; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.EthContext; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.task.WaitForPeersTask; |
||||||
|
import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; |
||||||
|
import org.hyperledger.besu.ethereum.eth.sync.tasks.RetryingGetHeaderFromPeerByHashTask; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
import org.hyperledger.besu.plugin.services.MetricsSystem; |
||||||
|
|
||||||
|
import java.util.Optional; |
||||||
|
import java.util.concurrent.CompletableFuture; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.function.Supplier; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public abstract class PivotSelectorFromBlock implements PivotBlockSelector { |
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PivotSelectorFromBlock.class); |
||||||
|
private final ProtocolContext protocolContext; |
||||||
|
private final ProtocolSchedule protocolSchedule; |
||||||
|
private final EthContext ethContext; |
||||||
|
private final MetricsSystem metricsSystem; |
||||||
|
private final GenesisConfigOptions genesisConfig; |
||||||
|
private final Supplier<Optional<ForkchoiceEvent>> forkchoiceStateSupplier; |
||||||
|
private final Runnable cleanupAction; |
||||||
|
|
||||||
|
private long lastNoFcuReceivedInfoLog = System.currentTimeMillis(); |
||||||
|
private static final long NO_FCU_RECEIVED_LOGGING_THRESHOLD = 60000L; |
||||||
|
|
||||||
|
private volatile Optional<BlockHeader> maybeCachedHeadBlockHeader = Optional.empty(); |
||||||
|
|
||||||
|
public PivotSelectorFromBlock( |
||||||
|
final ProtocolContext protocolContext, |
||||||
|
final ProtocolSchedule protocolSchedule, |
||||||
|
final EthContext ethContext, |
||||||
|
final MetricsSystem metricsSystem, |
||||||
|
final GenesisConfigOptions genesisConfig, |
||||||
|
final Supplier<Optional<ForkchoiceEvent>> forkchoiceStateSupplier, |
||||||
|
final Runnable cleanupAction) { |
||||||
|
this.protocolContext = protocolContext; |
||||||
|
this.protocolSchedule = protocolSchedule; |
||||||
|
this.ethContext = ethContext; |
||||||
|
this.metricsSystem = metricsSystem; |
||||||
|
this.genesisConfig = genesisConfig; |
||||||
|
this.forkchoiceStateSupplier = forkchoiceStateSupplier; |
||||||
|
this.cleanupAction = cleanupAction; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CompletableFuture<Void> prepareRetry() { |
||||||
|
// nothing to do
|
||||||
|
return CompletableFuture.completedFuture(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() { |
||||||
|
cleanupAction.run(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getMinRequiredBlockNumber() { |
||||||
|
return genesisConfig.getTerminalBlockNumber().orElse(0L); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getBestChainHeight() { |
||||||
|
final long localChainHeight = protocolContext.getBlockchain().getChainHeadBlockNumber(); |
||||||
|
|
||||||
|
return Math.max( |
||||||
|
forkchoiceStateSupplier |
||||||
|
.get() |
||||||
|
.map(ForkchoiceEvent::getHeadBlockHash) |
||||||
|
.map( |
||||||
|
headBlockHash -> |
||||||
|
maybeCachedHeadBlockHeader |
||||||
|
.filter( |
||||||
|
cachedBlockHeader -> cachedBlockHeader.getHash().equals(headBlockHash)) |
||||||
|
.map(BlockHeader::getNumber) |
||||||
|
.orElseGet( |
||||||
|
() -> { |
||||||
|
LOG.debug( |
||||||
|
"Downloading chain head block header by hash {}", headBlockHash); |
||||||
|
try { |
||||||
|
return waitForPeers(1) |
||||||
|
.thenCompose(unused -> downloadBlockHeader(headBlockHash)) |
||||||
|
.thenApply( |
||||||
|
blockHeader -> { |
||||||
|
maybeCachedHeadBlockHeader = Optional.of(blockHeader); |
||||||
|
return blockHeader.getNumber(); |
||||||
|
}) |
||||||
|
.get(20, TimeUnit.SECONDS); |
||||||
|
} catch (Throwable t) { |
||||||
|
LOG.debug( |
||||||
|
"Error trying to download chain head block header by hash {}", |
||||||
|
headBlockHash, |
||||||
|
t); |
||||||
|
} |
||||||
|
return null; |
||||||
|
})) |
||||||
|
.orElse(0L), |
||||||
|
localChainHeight); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Optional<FastSyncState> selectNewPivotBlock() { |
||||||
|
final Optional<ForkchoiceEvent> maybeForkchoice = forkchoiceStateSupplier.get(); |
||||||
|
if (maybeForkchoice.isPresent()) { |
||||||
|
Optional<Hash> pivotHash = getPivotHash(maybeForkchoice.get()); |
||||||
|
if (pivotHash.isPresent()) { |
||||||
|
LOG.info("Selecting new pivot block: {}", pivotHash); |
||||||
|
return Optional.of(new FastSyncState(pivotHash.get())); |
||||||
|
} |
||||||
|
} |
||||||
|
if (lastNoFcuReceivedInfoLog + NO_FCU_RECEIVED_LOGGING_THRESHOLD < System.currentTimeMillis()) { |
||||||
|
lastNoFcuReceivedInfoLog = System.currentTimeMillis(); |
||||||
|
LOG.info( |
||||||
|
"Waiting for consensus client, this may be because your consensus client is still syncing"); |
||||||
|
} |
||||||
|
LOG.debug("No finalized block hash announced yet"); |
||||||
|
return Optional.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
private CompletableFuture<BlockHeader> downloadBlockHeader(final Hash hash) { |
||||||
|
return RetryingGetHeaderFromPeerByHashTask.byHash( |
||||||
|
protocolSchedule, ethContext, hash, 0, metricsSystem) |
||||||
|
.getHeader() |
||||||
|
.whenComplete( |
||||||
|
(blockHeader, throwable) -> { |
||||||
|
if (throwable != null) { |
||||||
|
LOG.debug("Error downloading block header by hash {}", hash); |
||||||
|
} else { |
||||||
|
LOG.atDebug() |
||||||
|
.setMessage("Successfully downloaded pivot block header by hash {}") |
||||||
|
.addArgument(blockHeader::toLogString) |
||||||
|
.log(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private CompletableFuture<Void> waitForPeers(final int count) { |
||||||
|
final WaitForPeersTask waitForPeersTask = |
||||||
|
WaitForPeersTask.create(ethContext, count, metricsSystem); |
||||||
|
return waitForPeersTask.run(); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract Optional<Hash> getPivotHash(final ForkchoiceEvent forkchoiceEvent); |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* 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.fastsync; |
||||||
|
|
||||||
|
import org.hyperledger.besu.config.GenesisConfigOptions; |
||||||
|
import org.hyperledger.besu.consensus.merge.ForkchoiceEvent; |
||||||
|
import org.hyperledger.besu.datatypes.Hash; |
||||||
|
import org.hyperledger.besu.ethereum.ProtocolContext; |
||||||
|
import org.hyperledger.besu.ethereum.eth.manager.EthContext; |
||||||
|
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||||
|
import org.hyperledger.besu.plugin.services.MetricsSystem; |
||||||
|
|
||||||
|
import java.util.Optional; |
||||||
|
import java.util.function.Supplier; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public class PivotSelectorFromHeadBlock extends PivotSelectorFromBlock { |
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(PivotSelectorFromHeadBlock.class); |
||||||
|
|
||||||
|
public PivotSelectorFromHeadBlock( |
||||||
|
final ProtocolContext protocolContext, |
||||||
|
final ProtocolSchedule protocolSchedule, |
||||||
|
final EthContext ethContext, |
||||||
|
final MetricsSystem metricsSystem, |
||||||
|
final GenesisConfigOptions genesisConfig, |
||||||
|
final Supplier<Optional<ForkchoiceEvent>> forkchoiceStateSupplier, |
||||||
|
final Runnable cleanupAction) { |
||||||
|
super( |
||||||
|
protocolContext, |
||||||
|
protocolSchedule, |
||||||
|
ethContext, |
||||||
|
metricsSystem, |
||||||
|
genesisConfig, |
||||||
|
forkchoiceStateSupplier, |
||||||
|
cleanupAction); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected Optional<Hash> getPivotHash(final ForkchoiceEvent forkchoiceEvent) { |
||||||
|
Hash hash = forkchoiceEvent.getHeadBlockHash(); |
||||||
|
LOG.info("Returning head block hash {} as pivot", hash); |
||||||
|
return Optional.of(hash); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue