Enable Quorum/IBFT1 to Besu migration (#8262)

* Enable Quorum/IBFT1 to Besu migration

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Fix BftMining acceptance test

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Introduce delay after London fork update in BFT mining test to prevent timing issues

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Update besu/src/main/java/org/hyperledger/besu/controller/IbftLegacyBesuControllerBuilder.java

Co-authored-by: Matt Whitehead <matthew1001@hotmail.com>
Signed-off-by: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com>

* Review changes

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* update creating additional JSON RPC methods for all controllerbuidlers in consensus schedule

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Create ethprotocol manager and plugin factory for both consensus controllers in migration

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Refactor resource files

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* fix verification metadata

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* fix regression

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* update changelog

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Fix controller selection at the transition block

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Review changes

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

* Revert BftExtraData changes

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>

---------

Signed-off-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>
Signed-off-by: Bhanu Pulluri <59369753+pullurib@users.noreply.github.com>
Co-authored-by: Bhanu Pulluri <bhanu.pulluri@kaleido.io>
Co-authored-by: Matt Whitehead <matthew1001@hotmail.com>
Co-authored-by: Matt Whitehead <matthew.whitehead@kaleido.io>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
This commit is contained in:
Bhanu Pulluri
2025-03-12 10:08:51 -04:00
committed by GitHub
parent 083b1d3986
commit 2db46e964c
55 changed files with 1708 additions and 79 deletions

View File

@@ -37,6 +37,7 @@ dependencies {
implementation project(':consensus:clique')
implementation project(':consensus:common')
implementation project(':consensus:ibft')
implementation project(':consensus:ibftlegacy')
implementation project(':consensus:merge')
implementation project(':consensus:qbft')
implementation project(':consensus:qbft-core')

View File

@@ -173,6 +173,7 @@ public class Runner implements AutoCloseable {
LOG.info("Starting Ethereum main loop ... ");
natService.start();
networkRunner.start();
besuController.getMiningCoordinator().subscribe();
if (networkRunner.getNetwork().isP2pEnabled()) {
besuController.getSynchronizer().start();
}

View File

@@ -267,14 +267,19 @@ public class RlpBlockImporter implements Closeable {
private BlockHeader lookupPreviousHeader(
final MutableBlockchain blockchain, final BlockHeader header) {
return blockchain
.getBlockHeader(header.getParentHash())
.orElseThrow(
() ->
new IllegalStateException(
String.format(
"Block %s does not connect to the existing chain. Current chain head %s",
header.getNumber(), blockchain.getChainHeadBlockNumber())));
try {
return blockchain
.getBlockHeader(header.getParentHash())
.orElseThrow(
() ->
new IllegalStateException(
String.format(
"Block %s does not connect to the existing chain. Current chain head %s",
header.getNumber(), blockchain.getChainHeadBlockNumber())));
} catch (IllegalStateException e) {
LOG.info("Block {} does not connect to the existing chain.", header.getNumber());
}
return null;
}
@Override

View File

@@ -394,8 +394,7 @@ public class BesuController implements java.io.Closeable {
if (configOptions.isIbft2()) {
originalControllerBuilder = new IbftBesuControllerBuilder();
} else if (configOptions.isIbftLegacy()) {
throw new IllegalStateException(
"IBFT1 (legacy) is no longer supported. Consider using IBFT2 or QBFT.");
originalControllerBuilder = new IbftLegacyBesuControllerBuilder();
} else {
throw new IllegalStateException(
"Invalid genesis migration config. Migration is supported from IBFT (legacy) or IBFT2 to QBFT)");

View File

@@ -197,9 +197,10 @@ public class ConsensusScheduleBesuControllerBuilder extends BesuControllerBuilde
@Override
protected PluginServiceFactory createAdditionalPluginServices(
final Blockchain blockchain, final ProtocolContext protocolContext) {
return besuControllerBuilderSchedule
.get(0L)
.createAdditionalPluginServices(blockchain, protocolContext);
besuControllerBuilderSchedule
.values()
.forEach(b -> b.createAdditionalPluginServices(blockchain, protocolContext));
return new NoopPluginServiceFactory();
}
@Override
@@ -207,10 +208,14 @@ public class ConsensusScheduleBesuControllerBuilder extends BesuControllerBuilde
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final MiningConfiguration miningConfiguration) {
return besuControllerBuilderSchedule
.get(0L)
.createAdditionalJsonRpcMethodFactory(
protocolContext, protocolSchedule, miningConfiguration);
besuControllerBuilderSchedule
.values()
.forEach(
b ->
b.createAdditionalJsonRpcMethodFactory(
protocolContext, protocolSchedule, miningConfiguration));
return super.createAdditionalJsonRpcMethodFactory(
protocolContext, protocolSchedule, miningConfiguration);
}
@Override
@@ -218,7 +223,7 @@ public class ConsensusScheduleBesuControllerBuilder extends BesuControllerBuilde
final EthProtocolManager ethProtocolManager,
final Optional<SnapProtocolManager> maybeSnapProtocolManager) {
return besuControllerBuilderSchedule
.get(0L)
.get(besuControllerBuilderSchedule.keySet().stream().skip(1).findFirst().orElseThrow())
.createSubProtocolConfiguration(ethProtocolManager, maybeSnapProtocolManager);
}
@@ -240,20 +245,34 @@ public class ConsensusScheduleBesuControllerBuilder extends BesuControllerBuilde
final List<PeerValidator> peerValidators,
final Optional<MergePeerFilter> mergePeerFilter,
final ForkIdManager forkIdManager) {
return besuControllerBuilderSchedule
.get(0L)
.createEthProtocolManager(
protocolContext,
synchronizerConfiguration,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethContext,
ethMessages,
scheduler,
peerValidators,
mergePeerFilter,
forkIdManager);
besuControllerBuilderSchedule
.values()
.forEach(
b ->
b.createEthProtocolManager(
protocolContext,
synchronizerConfiguration,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethContext,
ethMessages,
scheduler,
peerValidators,
mergePeerFilter,
forkIdManager));
return super.createEthProtocolManager(
protocolContext,
synchronizerConfiguration,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethContext,
ethMessages,
scheduler,
peerValidators,
mergePeerFilter,
forkIdManager);
}
@Override

View File

@@ -0,0 +1,99 @@
/*
* 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.controller;
import org.hyperledger.besu.config.IbftLegacyConfigOptions;
import org.hyperledger.besu.consensus.common.EpochManager;
import org.hyperledger.besu.consensus.common.bft.BftBlockInterface;
import org.hyperledger.besu.consensus.common.bft.BftContext;
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider;
import org.hyperledger.besu.consensus.common.validator.blockbased.BlockValidatorProvider;
import org.hyperledger.besu.consensus.ibftlegacy.IbftExtraDataCodec;
import org.hyperledger.besu.consensus.ibftlegacy.IbftLegacyBlockInterface;
import org.hyperledger.besu.consensus.ibftlegacy.IbftProtocolSchedule;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.blockcreation.NoopMiningCoordinator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** The Ibft legacy besu controller builder. */
public class IbftLegacyBesuControllerBuilder extends BesuControllerBuilder {
private static final Logger LOG = LoggerFactory.getLogger(IbftLegacyBesuControllerBuilder.class);
private final BftBlockInterface blockInterface;
/** Default constructor */
public IbftLegacyBesuControllerBuilder() {
LOG.warn(
"IBFT1 is deprecated. This consensus configuration should be used only while migrating to another consensus mechanism.");
this.blockInterface = new IbftLegacyBlockInterface(new IbftExtraDataCodec());
}
@Override
protected MiningCoordinator createMiningCoordinator(
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final TransactionPool transactionPool,
final MiningConfiguration miningConfiguration,
final SyncState syncState,
final EthProtocolManager ethProtocolManager) {
return new NoopMiningCoordinator(miningConfiguration);
}
@Override
protected ProtocolSchedule createProtocolSchedule() {
return IbftProtocolSchedule.create(
genesisConfigOptions, privacyParameters, isRevertReasonEnabled, evmConfiguration);
}
@Override
protected BftContext createConsensusContext(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule) {
final IbftLegacyConfigOptions ibftConfig = genesisConfigOptions.getIbftLegacyConfigOptions();
final EpochManager epochManager = new EpochManager(ibftConfig.getEpochLength());
final ValidatorProvider validatorProvider =
BlockValidatorProvider.nonForkingValidatorProvider(
blockchain, epochManager, blockInterface);
return new BftContext(validatorProvider, epochManager, blockInterface);
}
@Override
protected PluginServiceFactory createAdditionalPluginServices(
final Blockchain blockchain, final ProtocolContext protocolContext) {
return new NoopPluginServiceFactory();
}
@Override
protected void validateContext(final ProtocolContext context) {
final BlockHeader genesisBlockHeader = context.getBlockchain().getGenesisBlock().getHeader();
if (blockInterface.validatorsInBlock(genesisBlockHeader).isEmpty()) {
LOG.warn("Genesis block contains no signers - chain will not progress.");
}
}
}

View File

@@ -96,7 +96,6 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.util.Subscribers;
import java.time.Duration;
@@ -152,7 +151,9 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder {
private ValidatorProvider createReadOnlyValidatorProvider(final Blockchain blockchain) {
checkNotNull(
transactionValidatorProvider, "transactionValidatorProvider should have been initialised");
final EpochManager epochManager = new EpochManager(qbftConfig.getEpochLength());
final long startBlock =
qbftConfig.getStartBlock().isPresent() ? qbftConfig.getStartBlock().getAsLong() : 0;
final EpochManager epochManager = new EpochManager(qbftConfig.getEpochLength(), startBlock);
// Must create our own voteTallyCache as using this would pollute the main voteTallyCache
final BlockValidatorProvider readOnlyBlockValidatorProvider =
BlockValidatorProvider.nonForkingValidatorProvider(
@@ -212,8 +213,16 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder {
qbftExtraDataCodec,
ethProtocolManager.ethContext().getScheduler());
final ValidatorProvider validatorProvider =
protocolContext.getConsensusContext(BftContext.class).getValidatorProvider();
final ValidatorProvider validatorProvider;
if (qbftConfig.getStartBlock().isPresent()) {
validatorProvider =
protocolContext
.getConsensusContext(BftContext.class, qbftConfig.getStartBlock().getAsLong())
.getValidatorProvider();
} else {
validatorProvider =
protocolContext.getConsensusContext(BftContext.class).getValidatorProvider();
}
final QbftValidatorProvider qbftValidatorProvider =
new QbftValidatorProviderAdaptor(validatorProvider);
@@ -316,7 +325,8 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder {
bftProcessor,
blockCreatorFactory,
blockchain,
bftEventQueue);
bftEventQueue,
syncState);
// Update the next block period in seconds according to the transition schedule
protocolContext
@@ -335,35 +345,6 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder {
.getEmptyBlockPeriodSeconds());
});
syncState.subscribeSyncStatus(
syncStatus -> {
if (syncState.syncTarget().isPresent()) {
// We're syncing so stop doing other stuff
LOG.info("Stopping QBFT mining coordinator while we are syncing");
miningCoordinator.stop();
} else {
LOG.info("Starting QBFT mining coordinator following sync");
miningCoordinator.enable();
miningCoordinator.start();
}
});
syncState.subscribeCompletionReached(
new BesuEvents.InitialSyncCompletionListener() {
@Override
public void onInitialSyncCompleted() {
LOG.info("Starting QBFT mining coordinator following initial sync");
miningCoordinator.enable();
miningCoordinator.start();
}
@Override
public void onInitialSyncRestart() {
// Nothing to do. The mining coordinator won't be started until
// sync has completed.
}
});
return miningCoordinator;
}
@@ -428,7 +409,9 @@ public class QbftBesuControllerBuilder extends BesuControllerBuilder {
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule) {
final EpochManager epochManager = new EpochManager(qbftConfig.getEpochLength());
final long startBlock =
qbftConfig.getStartBlock().isPresent() ? qbftConfig.getStartBlock().getAsLong() : 0;
final EpochManager epochManager = new EpochManager(qbftConfig.getEpochLength(), startBlock);
final BftValidatorOverrides validatorOverrides =
convertBftForks(genesisConfigOptions.getTransitions().getQbftForks());

View File

@@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.chainimport;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
@@ -21,6 +22,7 @@ import static org.mockito.Mockito.mock;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.components.BesuComponent;
import org.hyperledger.besu.config.GenesisConfig;
import org.hyperledger.besu.config.MergeConfiguration;
import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
@@ -40,9 +42,12 @@ import org.hyperledger.besu.testutil.TestClock;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.CompletionException;
import com.google.common.io.Resources;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -152,4 +157,46 @@ public final class RlpBlockImporterTest {
assertThat(result.count).isEqualTo(1);
assertThat(result.td).isEqualTo(UInt256.valueOf(34351349760L));
}
@Test
public void ibftImport() throws IOException {
final Path source = dataDir.resolve("ibft.blocks");
final String config =
Resources.toString(this.getClass().getResource("/ibft-genesis-2.json"), UTF_8);
try {
Files.write(
source,
Resources.toByteArray(this.getClass().getResource("/ibft.blocks")),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
} catch (final IOException ex) {
throw new IllegalStateException(ex);
}
final BesuController controller =
new BesuController.Builder()
.fromGenesisFile(GenesisConfig.fromConfig(config), SyncMode.FULL)
.synchronizerConfiguration(SynchronizerConfiguration.builder().build())
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.valueOf(1337))
.miningParameters(MiningConfiguration.newDefault())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)
.dataDirectory(dataDir)
.clock(TestClock.fixed())
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.evmConfiguration(EvmConfiguration.DEFAULT)
.networkConfiguration(NetworkingConfiguration.create())
.besuComponent(mock(BesuComponent.class))
.apiConfiguration(ImmutableApiConfiguration.builder().build())
.build();
final RlpBlockImporter.ImportResult result =
rlpBlockImporter.importBlockchain(source, controller, true);
// Don't count the Genesis block
assertThat(result.count).isEqualTo(100);
}
}

View File

@@ -98,6 +98,27 @@ public class BesuControllerTest {
.isInstanceOf(QbftBesuControllerBuilder.class);
}
@Test
public void createConsensusScheduleBesuControllerBuilderWhenMigratingFromIbftLegacyToQbft() {
final long qbftStartBlock = 10L;
mockGenesisConfigForMigration("ibftLegacy", OptionalLong.of(qbftStartBlock));
final BesuControllerBuilder besuControllerBuilder =
new BesuController.Builder().fromGenesisFile(genesisConfig, SyncMode.FULL);
assertThat(besuControllerBuilder).isInstanceOf(ConsensusScheduleBesuControllerBuilder.class);
final Map<Long, BesuControllerBuilder> besuControllerBuilderSchedule =
((ConsensusScheduleBesuControllerBuilder) besuControllerBuilder)
.getBesuControllerBuilderSchedule();
assertThat(besuControllerBuilderSchedule).containsKeys(0L, qbftStartBlock);
assertThat(besuControllerBuilderSchedule.get(0L))
.isInstanceOf(IbftLegacyBesuControllerBuilder.class);
assertThat(besuControllerBuilderSchedule.get(qbftStartBlock))
.isInstanceOf(QbftBesuControllerBuilder.class);
}
private void mockGenesisConfigForMigration(
final String consensus, final OptionalLong startBlock) {
when(genesisConfigOptions.isConsensusMigration()).thenReturn(true);
@@ -108,6 +129,11 @@ public class BesuControllerTest {
when(genesisConfigOptions.isIbft2()).thenReturn(true);
break;
}
case "ibftlegacy":
{
when(genesisConfigOptions.isIbftLegacy()).thenReturn(true);
break;
}
default:
fail("Invalid consensus algorithm");
}

View File

@@ -0,0 +1,65 @@
{
"nonce": "0x0",
"timestamp": "0x58ee40ba",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f86df86994a18182ee8ca476f2f0fb8170a1d4620edb39c5e194065541903bf3bb8c088a18046b441f5d286288c994d1e106d68cac92668b100f6f43791ddcb2c7588094d156777a1e1539fe654fc82266f41fd5d4aa548494efbbd8900222d7b2f75d081c3e7446a1f4fe10ce80c0",
"gasLimit": "700000000",
"gasUsed": "0x0",
"number": "0x0",
"difficulty": "0x1",
"coinbase": "0x0000000000000000000000000000000000000000",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"config": {
"chainId": 1337,
"homesteadBlock": 0,
"eip150Block": 20,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 30,
"eip158Block": 40,
"byzantiumBlock": 50,
"constantinopleBlock": 60,
"petersburgBlock": 70,
"istanbulBlock": 80,
"ibft": {
"epochlength": 1000,
"blockperiodseconds": 5,
"requesttimeoutseconds": 10,
"policy": 0,
"ceil2Nby3Block": 0,
"validatorcontractaddress": "0x0000000000000000000000000000000000000000"
},
"qbft": {
"epochLength": 30000,
"blockPeriodSeconds" : 1,
"requestTimeoutSeconds": 10,
"startBlock": 101
},
"txnSizeLimit": 64,
"maxCodeSize": 0,
"maxCodeSizeConfig": [
{
"block": 0,
"size": 64
}
],
"isMPS": false
},
"alloc": {
"0xde8e2ae09f2ee2c6c282c054b2384f8b5f9debee": {
"balance": "1000000000000000000000000000"
},
"0x23bcbca17fc4978909ab44ac82559c7d379aa006": {
"balance": "1000000000000000000000000000"
},
"0x870276532cca9f33e66273cfa494cf41e04b5a66": {
"balance": "1000000000000000000000000000"
},
"0x7d7fc9fdfa49e2db22fc6ebab593dcf3aeffbde8": {
"balance": "1000000000000000000000000000"
},
"0x4df76ad0678513846699056e0070c5f587580eb5": {
"balance": "1000000000000000000000000000"
}
}
}

BIN
besu/src/test/resources/ibft.blocks Normal file → Executable file

Binary file not shown.