Skip to content

Commit 2db46e9

Browse files
pulluribBhanu Pullurimatthew1001macfarla
authored
Enable Quorum/IBFT1 to Besu migration (#8262)
* Enable Quorum/IBFT1 to Besu migration Signed-off-by: Bhanu Pulluri <[email protected]> * Fix BftMining acceptance test Signed-off-by: Bhanu Pulluri <[email protected]> * Introduce delay after London fork update in BFT mining test to prevent timing issues Signed-off-by: Bhanu Pulluri <[email protected]> * Update besu/src/main/java/org/hyperledger/besu/controller/IbftLegacyBesuControllerBuilder.java Co-authored-by: Matt Whitehead <[email protected]> Signed-off-by: Bhanu Pulluri <[email protected]> * Review changes Signed-off-by: Bhanu Pulluri <[email protected]> * update creating additional JSON RPC methods for all controllerbuidlers in consensus schedule Signed-off-by: Bhanu Pulluri <[email protected]> * Create ethprotocol manager and plugin factory for both consensus controllers in migration Signed-off-by: Bhanu Pulluri <[email protected]> * Refactor resource files Signed-off-by: Bhanu Pulluri <[email protected]> * fix verification metadata Signed-off-by: Bhanu Pulluri <[email protected]> * fix regression Signed-off-by: Bhanu Pulluri <[email protected]> * update changelog Signed-off-by: Bhanu Pulluri <[email protected]> * Fix controller selection at the transition block Signed-off-by: Bhanu Pulluri <[email protected]> * Review changes Signed-off-by: Bhanu Pulluri <[email protected]> * Revert BftExtraData changes Signed-off-by: Bhanu Pulluri <[email protected]> --------- Signed-off-by: Bhanu Pulluri <[email protected]> Signed-off-by: Bhanu Pulluri <[email protected]> Co-authored-by: Bhanu Pulluri <[email protected]> Co-authored-by: Matt Whitehead <[email protected]> Co-authored-by: Matt Whitehead <[email protected]> Co-authored-by: Sally MacFarlane <[email protected]>
1 parent 083b1d3 commit 2db46e9

File tree

55 files changed

+1708
-79
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1708
-79
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
- Add TLS/mTLS options and configure the GraphQL HTTP service[#7910](https://github.com/hyperledger/besu/pull/7910)
4242
- Update `eth_getLogs` to return a `Block not found` error when the requested block is not found. [#8290](https://github.com/hyperledger/besu/pull/8290)
4343
- Change `Invalid block, unable to parse RLP` RPC error message to `Invalid block param (block not found)` [#8328](https://github.com/hyperledger/besu/pull/8328)
44+
- Add IBFT1 to QBFT migration capability [#8262](https://github.com/hyperledger/besu/pull/8262)
4445
- Support pending transaction score when saving and restoring txpool [#8363](https://github.com/hyperledger/besu/pull/8363)
4546
- Upgrade to execution-spec-tests v4.1.0 including better EIP-2537 coverage for BLS [#8402](https://github.com/hyperledger/besu/pull/8402)
4647
- Add era1 format to blocks import subcommand [#7935](https://github.com/hyperledger/besu/issues/7935)

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/net/AwaitNetPeerCount.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ public AwaitNetPeerCount(
3636

3737
@Override
3838
public void verify(final Node node) {
39-
WaitUtils.waitFor(() -> assertThat(node.execute(transaction)).isEqualTo(expectedPeerCount));
39+
WaitUtils.waitFor(50, () -> assertThat(node.execute(transaction)).isEqualTo(expectedPeerCount));
4040
}
4141
}

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java

+33
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,39 @@ public BesuNode createQbftNode(
490490
return create(builder.build());
491491
}
492492

493+
public BesuNode createQbftMigrationNode(
494+
final String name, final boolean fixedPort, final DataStorageFormat storageFormat)
495+
throws IOException {
496+
JsonRpcConfiguration rpcConfig = node.createJsonRpcWithQbftEnabledConfig(false);
497+
rpcConfig.addRpcApi("ADMIN,TXPOOL");
498+
if (fixedPort) {
499+
rpcConfig.setPort(
500+
Math.abs(name.hashCode() % 60000)
501+
+ 1024); // Generate a consistent port for p2p based on node name
502+
}
503+
504+
BesuNodeConfigurationBuilder builder =
505+
new BesuNodeConfigurationBuilder()
506+
.name(name)
507+
.miningEnabled()
508+
.jsonRpcConfiguration(rpcConfig)
509+
.webSocketConfiguration(node.createWebSocketEnabledConfig())
510+
.devMode(false)
511+
.dataStorageConfiguration(
512+
storageFormat == DataStorageFormat.FOREST
513+
? DataStorageConfiguration.DEFAULT_FOREST_CONFIG
514+
: DataStorageConfiguration.DEFAULT_BONSAI_CONFIG)
515+
.genesisConfigProvider(GenesisConfigurationFactory::createQbftMigrationGenesisConfig);
516+
if (fixedPort) {
517+
builder.p2pPort(
518+
Math.abs(name.hashCode() % 60000)
519+
+ 1024
520+
+ 500); // Generate a consistent port for p2p based on node name (+ 500 to avoid
521+
// clashing with RPC port or other nodes with a similar name)
522+
}
523+
return create(builder.build());
524+
}
525+
493526
public BesuNode createCustomGenesisNode(
494527
final String name, final String genesisPath, final boolean canBeBootnode) throws IOException {
495528
return createCustomGenesisNode(name, genesisPath, canBeBootnode, false);

acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/genesis/GenesisConfigurationFactory.java

+7
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ public static Optional<String> createQbftGenesisConfig(
9696
validators, template, QbftExtraDataCodec::createGenesisExtraDataString);
9797
}
9898

99+
public static Optional<String> createQbftMigrationGenesisConfig(
100+
final Collection<? extends RunnableNode> validators) {
101+
final String template = readGenesisFile("/qbft/migration-ibft1/qbft-migration.json");
102+
return updateGenesisExtraData(
103+
validators, template, QbftExtraDataCodec::createGenesisExtraDataString);
104+
}
105+
99106
@SuppressWarnings("unchecked")
100107
public static Optional<String> createQbftValidatorContractGenesisConfig(
101108
final Collection<? extends RunnableNode> validators) throws UncheckedIOException {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright contributors to Hyperledger Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.tests.acceptance;
16+
17+
import static org.apache.logging.log4j.util.LoaderUtil.getClassLoader;
18+
19+
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
20+
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
21+
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
22+
23+
import java.io.File;
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.nio.file.StandardCopyOption;
29+
30+
import org.junit.jupiter.api.Test;
31+
32+
public class QuorumIBFTMigrationTest extends AcceptanceTestBase {
33+
34+
public static void copyKeyFilesToNodeDataDirs(final BesuNode... nodes) throws IOException {
35+
for (BesuNode node : nodes) {
36+
copyKeyFile(node, "key");
37+
copyKeyFile(node, "key.pub");
38+
}
39+
}
40+
41+
private static void copyKeyFile(final BesuNode node, final String keyFileName)
42+
throws IOException {
43+
String resourceFileName = "qbft/migration-ibft1/" + node.getName() + keyFileName;
44+
try (InputStream keyFileStream = getClassLoader().getResourceAsStream(resourceFileName)) {
45+
if (keyFileStream == null) {
46+
throw new IOException("Resource not found: " + resourceFileName);
47+
}
48+
Path targetPath = node.homeDirectory().resolve(keyFileName);
49+
Files.createDirectories(targetPath.getParent());
50+
Files.copy(keyFileStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
51+
}
52+
}
53+
54+
public static void runBesuCommand(final Path dataPath) throws IOException, InterruptedException {
55+
ProcessBuilder processBuilder =
56+
new ProcessBuilder(
57+
"../../build/install/besu/bin/besu",
58+
"--genesis-file",
59+
"src/test/resources/qbft/migration-ibft1/qbft-migration.json",
60+
"--data-path",
61+
dataPath.toString(),
62+
"--data-storage-format",
63+
"FOREST",
64+
"blocks",
65+
"import",
66+
"src/test/resources/qbft/migration-ibft1/ibft.blocks");
67+
68+
processBuilder.directory(new File(System.getProperty("user.dir")));
69+
processBuilder.inheritIO(); // This will redirect the output to the console
70+
71+
Process process = processBuilder.start();
72+
int exitCode = process.waitFor();
73+
if (exitCode == 0) {
74+
System.out.println("Import command executed successfully.");
75+
} else {
76+
throw new RuntimeException("Import command execution failed with exit code: " + exitCode);
77+
}
78+
}
79+
80+
@Test
81+
public void shouldImportIBFTBlocksAndTransitionToQBFT() throws Exception {
82+
83+
// Create a mix of Bonsai and Forest DB nodes
84+
final BesuNode minerNode1 =
85+
besu.createQbftMigrationNode("miner1", false, DataStorageFormat.FOREST);
86+
final BesuNode minerNode2 =
87+
besu.createQbftMigrationNode("miner2", false, DataStorageFormat.FOREST);
88+
final BesuNode minerNode3 =
89+
besu.createQbftMigrationNode("miner3", false, DataStorageFormat.FOREST);
90+
final BesuNode minerNode4 =
91+
besu.createQbftMigrationNode("miner4", false, DataStorageFormat.FOREST);
92+
final BesuNode minerNode5 =
93+
besu.createQbftMigrationNode("miner5", false, DataStorageFormat.FOREST);
94+
95+
// Copy key files to the node datadirs
96+
// Use the key files saved in resources directory
97+
copyKeyFilesToNodeDataDirs(minerNode1, minerNode2, minerNode3, minerNode4, minerNode5);
98+
99+
// start one node and import blocks from import file
100+
// Use import file, genesis saved in resources directory
101+
102+
runBesuCommand(minerNode1.homeDirectory());
103+
104+
// After the import is done, start the rest of the nodes using the same genesis and respective
105+
// node keys
106+
107+
cluster.start(minerNode1, minerNode2, minerNode3, minerNode4, minerNode5);
108+
109+
// Check that the chain is progressing as expected
110+
cluster.verify(blockchain.reachesHeight(minerNode2, 1, 120));
111+
}
112+
113+
@Override
114+
public void tearDownAcceptanceTestBase() {
115+
cluster.stop();
116+
super.tearDownAcceptanceTestBase();
117+
}
118+
}

acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bftsoak/BftMiningSoakTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ public class BftMiningSoakTest extends ParameterizedBftTestBase {
4242

4343
private static final long ONE_MINUTE = Duration.of(1, ChronoUnit.MINUTES).toMillis();
4444

45-
private static final long THREE_MINUTES = Duration.of(3, ChronoUnit.MINUTES).toMillis();
46-
4745
private static final long TEN_SECONDS = Duration.of(10, ChronoUnit.SECONDS).toMillis();
4846

4947
static int getTestDurationMins() {
@@ -213,6 +211,8 @@ public void shouldBeStableDuringLongTest(
213211
upgradeToLondon(
214212
minerNode1, minerNode2, minerNode3, minerNode4, lastChainHeight.intValue() + 120);
215213

214+
cluster.verify(blockchain.reachesHeight(minerNode4, 1, 180));
215+
216216
previousStepEndTime = Instant.now();
217217

218218
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
@@ -241,7 +241,7 @@ public void shouldBeStableDuringLongTest(
241241
upgradeToShanghai(
242242
minerNode1, minerNode2, minerNode3, minerNode4, Instant.now().getEpochSecond() + 120);
243243

244-
Thread.sleep(THREE_MINUTES);
244+
cluster.verify(blockchain.reachesHeight(minerNode4, 1, 180));
245245

246246
SimpleStorageShanghai simpleStorageContractShanghai =
247247
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorageShanghai.class));
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0a46b91fe0c770a4355d1fec9ccd72d39264f46a74ed67a69a12ed4c265aa768
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
8093fb3200c783555ed487b8b5210ef3369b062a1f3ce5762d83d7a62205693d1e4f253e840ca48ec98d8f20c5b41bbbd43f34f87a1f68324ab51afe73732b96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
17c2aacfdf1f6defde20e6ae7132c6d3991e758af3799a307a75b38135678a48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
d81f65976ccc44c5e7e6ca859c5ede06b0f484f72c52d35dd8f7bd7581a8b7020d9ef45878696b4593daf5575b48dda5259f78f192a3445d5cc97c032660642f
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
917fb1b03034e5d7156b89bc2a3bc2aae1d146d2640d75e095f07110b0871bc1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
67785a2ac328648d94245abb25bdcfab853d06e68c70026d90f2fd5c8338b65c19235398eac205e4bbdb3fc1de9669ad6309e43ab203c8e7430664cea6451f56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3b3cbae8c034c4ba2d5f4df44faa013888216a6eabb7fffa0b224003ea770ba7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ec403552908986b5d9e4def3be9ddcb26d5b03def3b44ef2d5d728bb9a3028603405e7379c3e185cb2bc3e784548fcdd0e7616162029c3f407155b2fb25ba0ca
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
4ddfb30d4fcd6f5a9f959961f734e4c1469af223bc16b217eef0d3796e64973d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
28c02b375d62b0adef8213b76882dfec264d5dcbb0a8ba73f167bc718a6d02f1833af80ab9f51a5f007e5f3b8320e8fc5ab287d4bd59ad497f8949bf4abb9e80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"nonce": "0x0",
3+
"timestamp": "0x58ee40ba",
4+
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f86df86994a18182ee8ca476f2f0fb8170a1d4620edb39c5e194065541903bf3bb8c088a18046b441f5d286288c994d1e106d68cac92668b100f6f43791ddcb2c7588094d156777a1e1539fe654fc82266f41fd5d4aa548494efbbd8900222d7b2f75d081c3e7446a1f4fe10ce80c0",
5+
"gasLimit": "700000000",
6+
"gasUsed": "0x0",
7+
"number": "0x0",
8+
"difficulty": "0x1",
9+
"coinbase": "0x0000000000000000000000000000000000000000",
10+
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
11+
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
12+
"config": {
13+
"chainId": 1337,
14+
"homesteadBlock": 10,
15+
"eip150Block": 20,
16+
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
17+
"eip155Block": 25,
18+
"eip158Block": 30,
19+
"byzantiumBlock": 50,
20+
"constantinopleBlock": 60,
21+
"petersburgBlock": 70,
22+
"istanbulBlock": 80,
23+
"ibft": {
24+
"epochlength": 100,
25+
"blockperiodseconds": 5,
26+
"requesttimeoutseconds": 10,
27+
"policy": 0,
28+
"ceil2Nby3Block": 0,
29+
"validatorcontractaddress": "0x0000000000000000000000000000000000000000"
30+
},
31+
"qbft": {
32+
"epochLength": 30000,
33+
"blockPeriodSeconds" : 1,
34+
"requestTimeoutSeconds": 10,
35+
"startBlock": 101
36+
},
37+
"txnSizeLimit": 64,
38+
"maxCodeSize": 0,
39+
"maxCodeSizeConfig": [
40+
{
41+
"block": 0,
42+
"size": 64
43+
}
44+
]
45+
},
46+
"alloc": {
47+
"0xde8e2ae09f2ee2c6c282c054b2384f8b5f9debee": {
48+
"balance": "1000000000000000000000000000"
49+
},
50+
"0x23bcbca17fc4978909ab44ac82559c7d379aa006": {
51+
"balance": "1000000000000000000000000000"
52+
},
53+
"0x870276532cca9f33e66273cfa494cf41e04b5a66": {
54+
"balance": "1000000000000000000000000000"
55+
},
56+
"0x7d7fc9fdfa49e2db22fc6ebab593dcf3aeffbde8": {
57+
"balance": "1000000000000000000000000000"
58+
},
59+
"0x4df76ad0678513846699056e0070c5f587580eb5": {
60+
"balance": "1000000000000000000000000000"
61+
}
62+
}
63+
}

acceptance-tests/tests/src/test/resources/qbft/qbft.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@
3636
"number": "0x0",
3737
"gasUsed": "0x0",
3838
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
39-
}
39+
}

besu/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ dependencies {
3737
implementation project(':consensus:clique')
3838
implementation project(':consensus:common')
3939
implementation project(':consensus:ibft')
40+
implementation project(':consensus:ibftlegacy')
4041
implementation project(':consensus:merge')
4142
implementation project(':consensus:qbft')
4243
implementation project(':consensus:qbft-core')

besu/src/main/java/org/hyperledger/besu/Runner.java

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ public void startEthereumMainLoop() {
173173
LOG.info("Starting Ethereum main loop ... ");
174174
natService.start();
175175
networkRunner.start();
176+
besuController.getMiningCoordinator().subscribe();
176177
if (networkRunner.getNetwork().isP2pEnabled()) {
177178
besuController.getSynchronizer().start();
178179
}

besu/src/main/java/org/hyperledger/besu/chainimport/RlpBlockImporter.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,19 @@ private void logProgress(final long blockNum) {
267267

268268
private BlockHeader lookupPreviousHeader(
269269
final MutableBlockchain blockchain, final BlockHeader header) {
270-
return blockchain
271-
.getBlockHeader(header.getParentHash())
272-
.orElseThrow(
273-
() ->
274-
new IllegalStateException(
275-
String.format(
276-
"Block %s does not connect to the existing chain. Current chain head %s",
277-
header.getNumber(), blockchain.getChainHeadBlockNumber())));
270+
try {
271+
return blockchain
272+
.getBlockHeader(header.getParentHash())
273+
.orElseThrow(
274+
() ->
275+
new IllegalStateException(
276+
String.format(
277+
"Block %s does not connect to the existing chain. Current chain head %s",
278+
header.getNumber(), blockchain.getChainHeadBlockNumber())));
279+
} catch (IllegalStateException e) {
280+
LOG.info("Block {} does not connect to the existing chain.", header.getNumber());
281+
}
282+
return null;
278283
}
279284

280285
@Override

besu/src/main/java/org/hyperledger/besu/controller/BesuController.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,7 @@ private BesuControllerBuilder createConsensusScheduleBesuControllerBuilder(
394394
if (configOptions.isIbft2()) {
395395
originalControllerBuilder = new IbftBesuControllerBuilder();
396396
} else if (configOptions.isIbftLegacy()) {
397-
throw new IllegalStateException(
398-
"IBFT1 (legacy) is no longer supported. Consider using IBFT2 or QBFT.");
397+
originalControllerBuilder = new IbftLegacyBesuControllerBuilder();
399398
} else {
400399
throw new IllegalStateException(
401400
"Invalid genesis migration config. Migration is supported from IBFT (legacy) or IBFT2 to QBFT)");

0 commit comments

Comments
 (0)