Skip to content

Commit

Permalink
Add checkpoint sync (hyperledger#3849)
Browse files Browse the repository at this point in the history
Add a new way to synchronize which is X_CHECKPOINT. This mode is experimental so use it at your own risk. This mode allows you to do like a snapsync but starting from a specific checkpoint instead of starting from the genesis.

This checkpoint will be in the genesis configuration of each network. To add the checkpoint mechanism in a network you just have to add the checkpoint section in the genesis.

Currently there is a checkpoint for ropten, goerli and mainnet.

Mainnet on i3.2xlarge <6 hours
Goerli on i3.2xlarge <1 hours

Signed-off-by: Karim TAAM <karim.t2am@gmail.com>
  • Loading branch information
matkt authored Jun 3, 2022
1 parent 02d389a commit 6f85e1f
Show file tree
Hide file tree
Showing 61 changed files with 1,641 additions and 332 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 22.4.3

### Additions and Improvements
- \[EXPERIMENTAL\] Add checkpoint sync `--sync-mode="X_CHECKPOINT"` [#3849](https://github.com/hyperledger/besu/pull/3849)

### Bug Fixes

Expand Down
3 changes: 1 addition & 2 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -1881,7 +1880,7 @@ private void issueOptionWarnings() {
logger,
commandLine,
"--sync-mode",
!EnumSet.of(SyncMode.FAST, SyncMode.X_SNAP).contains(syncMode),
SyncMode.isFullSync(syncMode),
singletonList("--fast-sync-min-peers"));

if (!securityModuleName.equals(DEFAULT_SECURITY_MODULE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import static com.google.common.base.Preconditions.checkNotNull;

import org.hyperledger.besu.config.CheckpointConfigOptions;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.consensus.merge.FinalizedBlockHashSupplier;
Expand All @@ -36,6 +37,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;
Expand All @@ -48,6 +50,7 @@
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager;
import org.hyperledger.besu.ethereum.eth.peervalidation.CheckpointBlocksPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.DaoForkPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
Expand All @@ -59,6 +62,8 @@
import org.hyperledger.besu.ethereum.eth.sync.fastsync.PivotSelectorFromFinalizedBlock;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.PivotSelectorFromPeers;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.TransitionPivotSelector;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.checkpoint.Checkpoint;
import org.hyperledger.besu.ethereum.eth.sync.fastsync.checkpoint.ImmutableCheckpoint;
import org.hyperledger.besu.ethereum.eth.sync.fullsync.SyncTerminationCondition;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
Expand Down Expand Up @@ -87,7 +92,6 @@
import java.time.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -345,10 +349,27 @@ public BesuController build() {
syncConfig.getTransactionsParallelism(),
syncConfig.getComputationParallelism(),
metricsSystem);

final GenesisConfigOptions configOptions =
genesisConfig.getConfigOptions(genesisConfigOverrides);

Optional<Checkpoint> checkpoint = Optional.empty();
if (configOptions.getCheckpointOptions().isValid()) {
checkpoint =
Optional.of(
ImmutableCheckpoint.builder()
.blockHash(
Hash.fromHexString(configOptions.getCheckpointOptions().getHash().get()))
.blockNumber(configOptions.getCheckpointOptions().getNumber().getAsLong())
.totalDifficulty(
Difficulty.fromHexString(
configOptions.getCheckpointOptions().getTotalDifficulty().get()))
.build());
}

final EthContext ethContext = new EthContext(ethPeers, ethMessages, snapMessages, scheduler);
final boolean fastSyncEnabled =
EnumSet.of(SyncMode.FAST, SyncMode.X_SNAP).contains(syncConfig.getSyncMode());
final SyncState syncState = new SyncState(blockchain, ethPeers, fastSyncEnabled);
final boolean fastSyncEnabled = !SyncMode.isFullSync(syncConfig.getSyncMode());
final SyncState syncState = new SyncState(blockchain, ethPeers, fastSyncEnabled, checkpoint);
syncState.subscribeTTDReached(new PandaPrinter());

final TransactionPool transactionPool =
Expand Down Expand Up @@ -603,6 +624,17 @@ protected List<PeerValidator> createPeerValidators(final ProtocolSchedule protoc
protocolSchedule, metricsSystem, requiredBlock.getKey(), requiredBlock.getValue()));
}

final CheckpointConfigOptions checkpointConfigOptions =
genesisConfig.getConfigOptions(genesisConfigOverrides).getCheckpointOptions();
if (SyncMode.X_CHECKPOINT.equals(syncConfig.getSyncMode())
&& checkpointConfigOptions.isValid()) {
validators.add(
new CheckpointBlocksPeerValidator(
protocolSchedule,
metricsSystem,
checkpointConfigOptions.getNumber().orElseThrow(),
checkpointConfigOptions.getHash().map(Hash::fromHexString).orElseThrow()));
}
return validators;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.config.CheckpointConfigOptions;
import org.hyperledger.besu.config.EthashConfigOptions;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions;
Expand Down Expand Up @@ -69,6 +70,7 @@ public class BesuControllerBuilderTest {
@Mock GenesisConfigFile genesisConfigFile;
@Mock GenesisConfigOptions genesisConfigOptions;
@Mock EthashConfigOptions ethashConfigOptions;
@Mock CheckpointConfigOptions checkpointConfigOptions;
@Mock Keccak256ConfigOptions keccak256ConfigOptions;
@Mock SynchronizerConfiguration synchronizerConfiguration;
@Mock EthProtocolConfiguration ethProtocolConfiguration;
Expand Down Expand Up @@ -97,6 +99,7 @@ public void setup() {
when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions);
when(genesisConfigOptions.getThanosBlockNumber()).thenReturn(OptionalLong.empty());
when(genesisConfigOptions.getEthashConfigOptions()).thenReturn(ethashConfigOptions);
when(genesisConfigOptions.getCheckpointOptions()).thenReturn(checkpointConfigOptions);
when(ethashConfigOptions.getFixedDifficulty()).thenReturn(OptionalLong.empty());
when(genesisConfigOptions.getKeccak256ConfigOptions()).thenReturn(keccak256ConfigOptions);
when(keccak256ConfigOptions.getFixedDifficulty()).thenReturn(OptionalLong.empty());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.config.CheckpointConfigOptions;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.consensus.merge.MergeContext;
Expand Down Expand Up @@ -83,6 +84,7 @@ public class MergeBesuControllerBuilderTest {
@Mock GenesisConfigOptions genesisConfigOptions;
@Mock SynchronizerConfiguration synchronizerConfiguration;
@Mock EthProtocolConfiguration ethProtocolConfiguration;
@Mock CheckpointConfigOptions checkpointConfigOptions;
@Mock MiningParameters miningParameters;
@Mock ObservableMetricsSystem observableMetricsSystem;
@Mock PrivacyParameters privacyParameters;
Expand All @@ -108,6 +110,7 @@ public void setup() {
when(genesisConfigFile.getMixHash()).thenReturn(Hash.ZERO.toHexString());
when(genesisConfigFile.getNonce()).thenReturn(Long.toHexString(1));
when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions);
when(genesisConfigOptions.getCheckpointOptions()).thenReturn(checkpointConfigOptions);
when(genesisConfigOptions.getTerminalTotalDifficulty())
.thenReturn((Optional.of(UInt256.valueOf(100L))));
when(genesisConfigOptions.getThanosBlockNumber()).thenReturn(OptionalLong.empty());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.config.CheckpointConfigOptions;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.JsonQbftConfigOptions;
Expand Down Expand Up @@ -75,6 +76,7 @@ public class QbftBesuControllerBuilderTest {
@Mock private GenesisConfigOptions genesisConfigOptions;
@Mock private SynchronizerConfiguration synchronizerConfiguration;
@Mock private EthProtocolConfiguration ethProtocolConfiguration;
@Mock CheckpointConfigOptions checkpointConfigOptions;
@Mock private MiningParameters miningParameters;
@Mock private ObservableMetricsSystem observableMetricsSystem;
@Mock private PrivacyParameters privacyParameters;
Expand All @@ -98,6 +100,7 @@ public void setup() {
when(genesisConfigFile.getMixHash()).thenReturn(Hash.ZERO.toHexString());
when(genesisConfigFile.getNonce()).thenReturn(Long.toHexString(1));
when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions);
when(genesisConfigOptions.getCheckpointOptions()).thenReturn(checkpointConfigOptions);
when(storageProvider.createBlockchainStorage(any()))
.thenReturn(
new KeyValueStoragePrefixedKeyBlockchainStorage(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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.config;

import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableMap;

public class CheckpointConfigOptions {

public static final CheckpointConfigOptions DEFAULT =
new CheckpointConfigOptions(JsonUtil.createEmptyObjectNode());

private final ObjectNode checkpointConfigRoot;

CheckpointConfigOptions(final ObjectNode checkpointConfigRoot) {
this.checkpointConfigRoot = checkpointConfigRoot;
}

public Optional<String> getTotalDifficulty() {
return JsonUtil.getString(checkpointConfigRoot, "totaldifficulty");
}

public OptionalLong getNumber() {
return JsonUtil.getLong(checkpointConfigRoot, "number");
}

public Optional<String> getHash() {
return JsonUtil.getString(checkpointConfigRoot, "hash");
}

public boolean isValid() {
return getTotalDifficulty().isPresent() && getNumber().isPresent() && getHash().isPresent();
}

Map<String, Object> asMap() {
final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
getTotalDifficulty().ifPresent(l -> builder.put("totaldifficulty", l));
getNumber().ifPresent(l -> builder.put("number", l));
getHash().ifPresent(l -> builder.put("hash", l));
return builder.build();
}

@Override
public String toString() {
return "CheckpointConfigOptions{" + "checkpointConfigRoot=" + checkpointConfigRoot + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ default boolean isConsensusMigration() {

IbftLegacyConfigOptions getIbftLegacyConfigOptions();

CheckpointConfigOptions getCheckpointOptions();

CliqueConfigOptions getCliqueConfigOptions();

BftConfigOptions getBftConfigOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
private static final String EC_CURVE_CONFIG_KEY = "eccurve";
private static final String TRANSITIONS_CONFIG_KEY = "transitions";
private static final String DISCOVERY_CONFIG_KEY = "discovery";
private static final String CHECKPOINT_CONFIG_KEY = "checkpoint";

private final ObjectNode configRoot;
private final Map<String, String> configOverrides = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
Expand Down Expand Up @@ -168,6 +169,13 @@ public DiscoveryOptions getDiscoveryOptions() {
.orElse(DiscoveryOptions.DEFAULT);
}

@Override
public CheckpointConfigOptions getCheckpointOptions() {
return JsonUtil.getObjectNode(configRoot, CHECKPOINT_CONFIG_KEY)
.map(CheckpointConfigOptions::new)
.orElse(CheckpointConfigOptions.DEFAULT);
}

@Override
public CliqueConfigOptions getCliqueConfigOptions() {
return JsonUtil.getObjectNode(configRoot, CLIQUE_CONFIG_KEY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ public IbftLegacyConfigOptions getIbftLegacyConfigOptions() {
return IbftLegacyConfigOptions.DEFAULT;
}

@Override
public CheckpointConfigOptions getCheckpointOptions() {
return CheckpointConfigOptions.DEFAULT;
}

@Override
public CliqueConfigOptions getCliqueConfigOptions() {
return CliqueConfigOptions.DEFAULT;
Expand Down
6 changes: 6 additions & 0 deletions config/src/main/resources/goerli.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
"enode://d4f764a48ec2a8ecf883735776fdefe0a3949eb0ca476bd7bc8d0954a9defe8fea15ae5da7d40b5d2d59ce9524a99daedadf6da6283fca492cc80b53689fb3b3@46.4.99.122:32109",
"enode://d2b720352e8216c9efc470091aa91ddafc53e222b32780f505c817ceef69e01d5b0b0797b69db254c586f493872352f5a022b4d8479a00fc92ec55f9ad46a27e@88.99.70.182:30303"
]
},
"checkpoint": {
"hash": "0x2ae30061bdfc7f6dad5b07361dce436502eb0fde68645de12bae4929be619188",
"number": 6720000,
"totalDifficulty": "0x967F81",
"_comment": "must be the beginning of an epoch"
}
},
"coinbase":"0x0000000000000000000000000000000000000000",
Expand Down
5 changes: 5 additions & 0 deletions config/src/main/resources/mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
"enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"
]
},
"checkpoint": {
"hash": "0x844d581cb00058d19f0584fb582fa2de208876ee56bbae27446a679baf4633f4",
"number": 14700000,
"totalDifficulty": "0xA2539264C62BF98CFC6"
}
},
"nonce": "0x42",
Expand Down
5 changes: 5 additions & 0 deletions config/src/main/resources/ropsten.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
"enode://30b7ab30a01c124a6cceca36863ece12c4f5fa68e3ba9b0b51407ccc002eeed3b3102d20a88f1c1d3c3154e2449317b8ef95090e77b312d5cc39354f86d5d606@52.176.7.10:30303",
"enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303"
]
},
"checkpoint": {
"hash": "0x43de216f876d897e59b9757dd24186e5b53be28bc425ca6a966335b48daaa50c",
"number": 12200000,
"totalDifficulty": "0x928D05243C1CF4"
}
},
"nonce": "0x0000000000000042",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ public Hash hash(final BlockHeader header) {
public CliqueExtraData parseExtraData(final BlockHeader header) {
return CliqueExtraData.decodeRaw(header);
}

@Override
public int getCheckPointWindowSize(final BlockHeader blockHeader) {
return CliqueExtraData.decode(blockHeader).getValidators().size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ public Hash hash(final BlockHeader header) {
public BftExtraData parseExtraData(final BlockHeader header) {
return bftExtraDataCodec.decodeRaw(header.getExtraData());
}

@Override
public int getCheckPointWindowSize(final BlockHeader header) {
return bftExtraDataCodec.decodeRaw(header.getExtraData()).getValidators().size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ public Hash hash(final BlockHeader header) {
public IbftExtraData parseExtraData(final BlockHeader header) {
return IbftExtraData.decodeRaw(header.getExtraData());
}

@Override
public int getCheckPointWindowSize(final BlockHeader header) {
return IbftExtraData.decodeRaw(header.getExtraData()).getValidators().size();
}
}
Loading

0 comments on commit 6f85e1f

Please sign in to comment.