Skip to content

Commit

Permalink
fix: update fork choice state after checkpoint syncing
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkAfCod authored and GrapeBaBa committed Apr 30, 2024
1 parent 8054c80 commit 7909341
Showing 1 changed file with 74 additions and 54 deletions.
128 changes: 74 additions & 54 deletions hildr-node/src/main/java/io/optimism/runner/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,7 @@ public void checkpointSync() throws ExecutionException, InterruptedException {
}
} else {
LOGGER.info("finding the latest epoch boundary to use as checkpoint");
BigInteger blockNumber;
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<BigInteger> blockNumberFuture =
scope.fork(TracerTaskWrapper.wrap(() -> new Request<>(
"eth_blockNumber",
Collections.<String>emptyList(),
checkpointSyncUrl,
EthBlockNumber.class)
.send()
.getBlockNumber()));
scope.join();
scope.throwIfFailed();
blockNumber = blockNumberFuture.get();
}
BigInteger blockNumber = getEthBlockNumber(checkpointSyncUrl);

while (isRunning() && !this.isShutdownTriggered) {
Tuple2<Boolean, OpEthBlock> isEpochBoundary =
Expand Down Expand Up @@ -238,11 +225,47 @@ public void checkpointSync() throws ExecutionException, InterruptedException {
}
if (l2CheckpointBlock != null && l2CheckpointBlock.getBlock() != null) {
LOGGER.warn("finalized head is above the checkpoint block");
this.startDriver();
ForkchoiceState forkchoiceState = ForkchoiceState.fromSingleHead(checkpointHash);
updateForkChoiceState(forkchoiceState);
waitDriverRunning();
return;
}

// this is a temporary fix to allow execution layer peering to work
addTrustedPeerToL2Engine(l2Provider);

ExecutionPayload checkpointPayload =
ExecutionPayload.fromL2Block(checkpointBlock.getBlock(), this.config.chainConfig());
ForkchoiceState forkchoiceState = ForkchoiceState.fromSingleHead(checkpointHash);

newPayloadToCheckpoint(checkpointPayload);
updateForkChoiceState(forkchoiceState);

LOGGER.info("syncing execution client to the checkpoint block...");
while (isRunning() && !this.isShutdownTriggered) {
BigInteger blockNumber;
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<BigInteger> blockNumberFuture = scope.fork(TracerTaskWrapper.wrap(
() -> l2Provider.ethBlockNumber().send().getBlockNumber()));

scope.join();
scope.throwIfFailed();
blockNumber = blockNumberFuture.get();
}
if (blockNumber.compareTo(checkpointPayload.blockNumber()) >= 0) {
break;
} else {
Thread.sleep(Duration.ofSeconds(3L));
}
}

// after syncing to the checkpoint block, update the forkchoice state
updateForkChoiceState(forkchoiceState);
LOGGER.info("execution client successfully synced to the checkpoint block");
waitDriverRunning();
}

private static void addTrustedPeerToL2Engine(Web3j l2Provider) throws InterruptedException, ExecutionException {
// TODO: use a list of whitelisted bootnodes instead
LOGGER.info("adding trusted peer to the execution layer");
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Expand All @@ -255,10 +278,41 @@ public void checkpointSync() throws ExecutionException, InterruptedException {
throw new TrustedPeerAddedException("could not add peer");
}
}
}

ExecutionPayload checkpointPayload =
ExecutionPayload.fromL2Block(checkpointBlock.getBlock(), this.config.chainConfig());
/**
* snap sync.
*
* @throws InterruptedException the interrupted exception
*/
public void executionLayerSync() throws InterruptedException, ExecutionException {
LOGGER.info("execution layer sync");
waitDriverRunning();
}

private void startDriver() throws InterruptedException {
driver.startAsync().awaitRunning();
latch.await();
}

private BigInteger getEthBlockNumber(Web3jService web3jService) throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<BigInteger> blockNumberFuture =
scope.fork(TracerTaskWrapper.wrap(() -> new Request<>(
"eth_blockNumber",
Collections.<String>emptyList(),
web3jService,
EthBlockNumber.class)
.send()
.getBlockNumber()));
scope.join();
scope.throwIfFailed();
return blockNumberFuture.get();
}
}

private void newPayloadToCheckpoint(ExecutionPayload checkpointPayload)
throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<OpEthPayloadStatus> payloadStatusFuture =
scope.fork(TracerTaskWrapper.wrap(() -> engineApi.newPayload(checkpointPayload)));
Expand All @@ -271,8 +325,10 @@ public void checkpointSync() throws ExecutionException, InterruptedException {
throw new InvalidExecutionPayloadException("the provided checkpoint payload is invalid");
}
}
}

ForkchoiceState forkchoiceState = ForkchoiceState.fromSingleHead(checkpointHash);
private void updateForkChoiceState(ForkchoiceState forkchoiceState)
throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<OpEthForkChoiceUpdate> forkChoiceUpdateFuture =
scope.fork(TracerTaskWrapper.wrap(() -> engineApi.forkchoiceUpdated(forkchoiceState, null)));
Expand All @@ -288,42 +344,6 @@ public void checkpointSync() throws ExecutionException, InterruptedException {
throw new ForkchoiceUpdateException("could not accept forkchoice, exiting");
}
}

LOGGER.info("syncing execution client to the checkpoint block...");
while (isRunning() && !this.isShutdownTriggered) {
BigInteger blockNumber;
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
StructuredTaskScope.Subtask<BigInteger> blockNumberFuture = scope.fork(TracerTaskWrapper.wrap(
() -> l2Provider.ethBlockNumber().send().getBlockNumber()));

scope.join();
scope.throwIfFailed();
blockNumber = blockNumberFuture.get();
}
if (blockNumber.compareTo(checkpointPayload.blockNumber()) >= 0) {
break;
} else {
Thread.sleep(Duration.ofSeconds(3L));
}
}

LOGGER.info("execution client successfully synced to the checkpoint block");
waitDriverRunning();
}

/**
* snap sync.
*
* @throws InterruptedException the interrupted exception
*/
public void executionLayerSync() throws InterruptedException, ExecutionException {
LOGGER.info("execution layer sync");
waitDriverRunning();
}

private void startDriver() throws InterruptedException {
driver.startAsync().awaitRunning();
latch.await();
}

private Tuple2<Boolean, OpEthBlock> isEpochBoundary(String blockHash, Web3jService checkpointSyncUrl)
Expand Down

0 comments on commit 7909341

Please sign in to comment.