Add new acceptance test to soak test BFT chains (#7023)

* Add new acceptance test to soak test BFT chains

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Spotless

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Tidy up a little with re-usable start and stop functions with built in delays

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Add shanghai version of Simple Storage contract

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Put commented gradle code back in. Fix the web3j example commands in .sol files

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Spotless

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Set publication artifacts to avoid clash

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Exclude from regular acceptance tests

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Add shanghai fork to the test. Stall the chain for less time to reduce the time taken to mine new blocks

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Tidy up

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update acceptance-tests/tests/shanghai/build.gradle

Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
Signed-off-by: Matt Whitehead <matthew1001@hotmail.com>

* Tidy up var names

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Fix ports for IBFT2 as well as QBFT

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Remove maven publish spec, disable jar building for shanghai contract project

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* web3j version

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Make fixed port optional when creating a BFT node

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Only check artifact coordinates for those starting 'org.*'

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

---------

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matt Whitehead <matthew1001@hotmail.com>
Signed-off-by: Matt Whitehead <matthew.whitehead@kaleido.io>
Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/6972/head
Matt Whitehead 5 months ago committed by GitHub
parent 04f304fbb5
commit b1ac5acd60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 49
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  2. 32
      acceptance-tests/tests/build.gradle
  3. 2
      acceptance-tests/tests/contracts/CrossContractReader.sol
  4. 2
      acceptance-tests/tests/contracts/EventEmitter.sol
  5. 2
      acceptance-tests/tests/contracts/RemoteSimpleStorage.sol
  6. 2
      acceptance-tests/tests/contracts/RevertReason.sol
  7. 2
      acceptance-tests/tests/contracts/SimpleStorage.sol
  8. 21
      acceptance-tests/tests/shanghai/build.gradle
  9. 31
      acceptance-tests/tests/shanghai/shanghaicontracts/SimpleStorageShanghai.sol
  10. 15
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/BftAcceptanceTestParameterization.java
  11. 351
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bftsoak/BftMiningSoakTest.java
  12. 2
      build.gradle
  13. 1
      settings.gradle

@ -461,16 +461,30 @@ public class BesuNodeFactory {
.build());
}
public BesuNode createIbft2Node(final String name) throws IOException {
return create(
public BesuNode createIbft2Node(final String name, final boolean fixedPort) throws IOException {
JsonRpcConfiguration rpcConfig = node.createJsonRpcWithIbft2EnabledConfig(false);
rpcConfig.addRpcApi("ADMIN,TXPOOL");
if (fixedPort) {
rpcConfig.setPort(
Math.abs(name.hashCode() % 60000)
+ 1024); // Generate a consistent port for p2p based on node name
}
BesuNodeConfigurationBuilder builder =
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(false))
.jsonRpcConfiguration(rpcConfig)
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(GenesisConfigurationFactory::createIbft2GenesisConfig)
.build());
.genesisConfigProvider(GenesisConfigurationFactory::createIbft2GenesisConfig);
if (fixedPort) {
builder.p2pPort(
Math.abs(name.hashCode() % 60000)
+ 1024
+ 500); // Generate a consistent port for p2p based on node name (+ 500 to avoid
// clashing with RPC port or other nodes with a similar name)
}
return create(builder.build());
}
public BesuNode createQbftNodeWithTLS(final String name, final String type) throws IOException {
@ -498,16 +512,31 @@ public class BesuNodeFactory {
return createQbftNodeWithTLS(name, KeyStoreWrapper.KEYSTORE_TYPE_PKCS11);
}
public BesuNode createQbftNode(final String name) throws IOException {
return create(
public BesuNode createQbftNode(final String name, final boolean fixedPort) throws IOException {
JsonRpcConfiguration rpcConfig = node.createJsonRpcWithQbftEnabledConfig(false);
rpcConfig.addRpcApi("ADMIN,TXPOOL");
if (fixedPort) {
rpcConfig.setPort(
Math.abs(name.hashCode() % 60000)
+ 1024); // Generate a consistent port for p2p based on node name
}
BesuNodeConfigurationBuilder builder =
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithQbftEnabledConfig(false))
.jsonRpcConfiguration(rpcConfig)
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(GenesisConfigurationFactory::createQbftGenesisConfig)
.build());
.genesisConfigProvider(GenesisConfigurationFactory::createQbftGenesisConfig);
if (fixedPort) {
builder.p2pPort(
Math.abs(name.hashCode() % 60000)
+ 1024
+ 500); // Generate a consistent port for p2p based on node name (+ 500 to avoid
// clashing with RPC port or other nodes with a similar name)
}
return create(builder.build());
}
public BesuNode createCustomGenesisNode(

@ -24,6 +24,7 @@ solidity {
resolvePackages = false
// TODO: remove the forced version, when DEV network is upgraded to support latest forks
version '0.8.19'
evmVersion 'london'
}
dependencies {
@ -79,6 +80,7 @@ dependencies {
testImplementation 'org.web3j:besu'
testImplementation 'org.web3j:core'
testImplementation 'org.wiremock:wiremock'
testImplementation project(path: ':acceptance-tests:tests:shanghai')
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
}
@ -153,6 +155,7 @@ task acceptanceTestMainnet(type: Test) {
task acceptanceTestNotPrivacy(type: Test) {
inputs.property "integration.date", LocalTime.now() // so it runs at every invocation
exclude '**/privacy/**'
exclude '**/bftsoak/**'
useJUnitPlatform {}
@ -205,6 +208,35 @@ task acceptanceTestCliqueBft(type: Test) {
doFirst { mkdir "${buildDir}/jvmErrorLogs" }
}
task acceptanceTestBftSoak(type: Test) {
inputs.property "integration.date", LocalTime.now() // so it runs at every invocation
include '**/bftsoak/**'
useJUnitPlatform {}
dependsOn(rootProject.installDist)
setSystemProperties(test.getSystemProperties())
systemProperty 'acctests.runBesuAsProcess', 'true'
// Set to any time > 60 minutes to run the soak test for longer
// systemProperty 'acctests.soakTimeMins', '120'
systemProperty 'java.security.properties', "${buildDir}/resources/test/acceptanceTesting.security"
mustRunAfter rootProject.subprojects*.test
description = 'Runs BFT soak test.'
group = 'verification'
jvmArgs "-XX:ErrorFile=${buildDir}/jvmErrorLogs/java_err_pid%p.log"
testLogging {
exceptionFormat = 'full'
showStackTraces = true
showStandardStreams = true
showExceptions = true
showCauses = true
}
doFirst { mkdir "${buildDir}/jvmErrorLogs" }
}
task acceptanceTestPrivacy(type: Test) {
inputs.property "integration.date", LocalTime.now() // so it runs at every invocation
include '**/privacy/**'

@ -19,7 +19,7 @@ import "./EventEmitter.sol";
// compile with:
// solc CrossContractReader.sol --bin --abi --optimize --overwrite -o .
// then create web3j wrappers with:
// web3j solidity generate -b ./generated/CrossContractReader.bin -a ./generated/CrossContractReader.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
// web3j generate solidity -b ./generated/CrossContractReader.bin -a ./generated/CrossContractReader.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
contract CrossContractReader {
uint counter;

@ -17,7 +17,7 @@ pragma solidity >=0.7.0 <0.9.0;
// compile with:
// solc EventEmitter.sol --bin --abi --optimize --overwrite -o .
// then create web3j wrappers with:
// web3j solidity generate -b ./generated/EventEmitter.bin -a ./generated/EventEmitter.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
// web3j generate solidity -b ./generated/EventEmitter.bin -a ./generated/EventEmitter.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
contract EventEmitter {
address owner;
event stored(address _to, uint _amount);

@ -19,7 +19,7 @@ import "./SimpleStorage.sol";
// compile with:
// solc RemoteSimpleStorage.sol --bin --abi --optimize --overwrite -o .
// then create web3j wrappers with:
// web3j solidity generate -b ./generated/RemoteSimpleStorage.bin -a ./generated/RemoteSimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
// web3j generate solidity -b ./generated/RemoteSimpleStorage.bin -a ./generated/RemoteSimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
contract RemoteSimpleStorage {
SimpleStorage public simpleStorage;

@ -17,7 +17,7 @@ pragma solidity >=0.7.0 <0.9.0;
// compile with:
// solc RevertReason.sol --bin --abi --optimize --overwrite -o .
// then create web3j wrappers with:
// web3j solidity generate -b ./generated/RevertReason.bin -a ./generated/RevertReason.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
// web3j generate solidity -b ./generated/RevertReason.bin -a ./generated/RevertReason.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
contract RevertReason {
function revertWithRevertReason() public pure returns (bool) {

@ -17,7 +17,7 @@ pragma solidity >=0.7.0 <0.8.20;
// compile with:
// solc SimpleStorage.sol --bin --abi --optimize --overwrite -o .
// then create web3j wrappers with:
// web3j solidity generate -b ./generated/SimpleStorage.bin -a ./generated/SimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
// web3j generate solidity -b ./generated/SimpleStorage.bin -a ./generated/SimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
contract SimpleStorage {
uint data;

@ -0,0 +1,21 @@
plugins {
id 'org.web3j' version '4.11.3'
id 'org.web3j.solidity' version '0.4.1'
}
jar { enabled = true }
web3j {
generatedPackageName = 'org.hyperledger.besu.tests.web3j.generated'
}
sourceSets.main.solidity.srcDirs = [
"$projectDir/shanghaicontracts"
]
solidity {
resolvePackages = false
version '0.8.25'
evmVersion 'shanghai'
}

@ -0,0 +1,31 @@
/*
* 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
*/
pragma solidity >=0.8.20;
// compile with:
// solc SimpleStorageShanghai.sol --bin --abi --optimize --overwrite -o .
// then create web3j wrappers with:
// web3j generate solidity -b ./SimpleStorageShanghai.bin -a ./SimpleStorageShanghai.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
contract SimpleStorageShanghai {
uint data;
function set(uint value) public {
data = value;
}
function get() public view returns (uint) {
return data;
}
}

@ -38,7 +38,14 @@ public class BftAcceptanceTestParameterization {
@FunctionalInterface
public interface NodeCreator {
BesuNode create(BesuNodeFactory factory, String name) throws Exception;
BesuNode create(BesuNodeFactory factory, String name, boolean fixedPort) throws Exception;
}
@FunctionalInterface
public interface FixedPortNodeCreator {
BesuNode createFixedPort(BesuNodeFactory factory, String name, boolean fixedPort)
throws Exception;
}
@FunctionalInterface
@ -57,7 +64,11 @@ public class BftAcceptanceTestParameterization {
}
public BesuNode createNode(BesuNodeFactory factory, String name) throws Exception {
return creatorFn.create(factory, name);
return creatorFn.create(factory, name, false);
}
public BesuNode createNodeFixedPort(BesuNodeFactory factory, String name) throws Exception {
return creatorFn.create(factory, name, true);
}
public BesuNode createNodeWithValidators(

@ -0,0 +1,351 @@
/*
* 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.tests.acceptance.bftsoak;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.config.JsonUtil;
import org.hyperledger.besu.tests.acceptance.bft.BftAcceptanceTestParameterization;
import org.hyperledger.besu.tests.acceptance.bft.ParameterizedBftTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import org.hyperledger.besu.tests.web3j.generated.SimpleStorage;
import org.hyperledger.besu.tests.web3j.generated.SimpleStorageShanghai;
import java.math.BigInteger;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class BftMiningSoakTest extends ParameterizedBftTestBase {
private final int NUM_STEPS = 5;
private final int MIN_TEST_TIME_MINS = 60;
private static final long ONE_MINUTE = Duration.of(1, ChronoUnit.MINUTES).toMillis();
private static final long THREE_MINUTES = Duration.of(1, ChronoUnit.MINUTES).toMillis();
private static final long TEN_SECONDS = Duration.of(10, ChronoUnit.SECONDS).toMillis();
static int getTestDurationMins() {
// Use a default soak time of 60 mins
return Integer.getInteger("acctests.soakTimeMins", 60);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("factoryFunctions")
public void shouldBeStableDuringLongTest(
final String testName, final BftAcceptanceTestParameterization nodeFactory) throws Exception {
setUp(testName, nodeFactory);
// There is a minimum amount of time the test can be run for, due to hard coded delays
// in between certain steps. There should be no upper-limit to how long the test is run for
assertThat(getTestDurationMins()).isGreaterThanOrEqualTo(MIN_TEST_TIME_MINS);
final BesuNode minerNode1 = nodeFactory.createNodeFixedPort(besu, "miner1");
final BesuNode minerNode2 = nodeFactory.createNodeFixedPort(besu, "miner2");
final BesuNode minerNode3 = nodeFactory.createNodeFixedPort(besu, "miner3");
final BesuNode minerNode4 = nodeFactory.createNodeFixedPort(besu, "miner4");
// Each step should be given a minimum of 3 minutes to complete successfully. If the time
// give to run the soak test results in a time-per-step lower than this then the time
// needs to be increased.
assertThat(getTestDurationMins() / NUM_STEPS).isGreaterThanOrEqualTo(3);
cluster.start(minerNode1, minerNode2, minerNode3, minerNode4);
cluster.verify(blockchain.reachesHeight(minerNode1, 1, 85));
// Setup
// Deploy a contract that we'll invoke periodically to ensure state
// is correct during the test, especially after stopping nodes and
// applying new forks.
SimpleStorage simpleStorageContract =
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorage.class));
// Check the contract address is as expected for this sender & nonce
contractVerifier
.validTransactionReceipt("0x42699a7612a82f1d9c36148af9c77354759b210b")
.verify(simpleStorageContract);
// Before upgrading to newer forks, try creating a shanghai-evm contract and check that
// the transaction fails
try {
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorageShanghai.class));
Assertions.fail("Shanghai transaction should not be executed on a pre-shanghai chain");
} catch (RuntimeException e) {
assertThat(e.getMessage())
.contains(
"Revert reason: 'Transaction processing could not be completed due to an exception'");
}
// Should initially be set to 0
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(0));
// Set to something new
simpleStorageContract.set(BigInteger.valueOf(101)).send();
// Check the state of the contract has updated correctly. We'll set & get this several times
// during the test
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(101));
// Step 1
// Run for the configured time period, periodically checking that
// the chain is progressing as expected
BigInteger chainHeight = minerNode1.execute(ethTransactions.blockNumber());
assertThat(chainHeight.compareTo(BigInteger.ZERO)).isGreaterThanOrEqualTo(1);
BigInteger lastChainHeight = chainHeight;
Instant startTime = Instant.now();
Instant nextStepEndTime = startTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
Thread.sleep(ONE_MINUTE);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
// With 1-second block period chain height should have moved on by at least 50 blocks
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(50))))
.isGreaterThanOrEqualTo(1);
lastChainHeight = chainHeight;
}
Instant previousStepEndTime = Instant.now();
// Step 2
// Stop one of the nodes, check that the chain continues mining
// blocks
stopNode(minerNode4);
nextStepEndTime =
previousStepEndTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
Thread.sleep(ONE_MINUTE);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
// Chain height should have moved on by at least 5 blocks
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(20))))
.isGreaterThanOrEqualTo(1);
lastChainHeight = chainHeight;
}
previousStepEndTime = Instant.now();
// Step 3
// Stop another one of the nodes, check that the chain now stops
// mining blocks
stopNode(minerNode3);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
lastChainHeight = chainHeight;
// Leave the chain stalled for 3 minutes. Check no new blocks are mined. Then
// resume the other validators.
nextStepEndTime = previousStepEndTime.plus(3, ChronoUnit.MINUTES);
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
Thread.sleep(ONE_MINUTE);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
// Chain height should not have moved on
assertThat(chainHeight.equals(lastChainHeight)).isTrue();
}
// Step 4
// Restart both of the stopped nodes. Check that the chain resumes
// mining blocks
startNode(minerNode3);
startNode(minerNode4);
previousStepEndTime = Instant.now();
// This step gives the stalled chain time to re-sync and agree on the next BFT round
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
nextStepEndTime =
previousStepEndTime.plus((getTestDurationMins() / NUM_STEPS), ChronoUnit.MINUTES);
lastChainHeight = chainHeight;
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
Thread.sleep(ONE_MINUTE);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
lastChainHeight = chainHeight;
}
previousStepEndTime = Instant.now();
// By this loop it should be producing blocks again
nextStepEndTime =
previousStepEndTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
Thread.sleep(ONE_MINUTE);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
// Chain height should have moved on by at least 1 block
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(1))))
.isGreaterThanOrEqualTo(1);
lastChainHeight = chainHeight;
}
// Update our smart contract before upgrading from berlin to london
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(101));
simpleStorageContract.set(BigInteger.valueOf(201)).send();
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(201));
// Upgrade the chain from berlin to london in 120 blocks time
upgradeToLondon(
minerNode1, minerNode2, minerNode3, minerNode4, lastChainHeight.intValue() + 120);
previousStepEndTime = Instant.now();
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
nextStepEndTime =
previousStepEndTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
lastChainHeight = chainHeight;
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
Thread.sleep(ONE_MINUTE);
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
// Chain height should have moved on by at least 50 blocks
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(50))))
.isGreaterThanOrEqualTo(1);
lastChainHeight = chainHeight;
}
// Check that the state of our smart contract is still correct
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(201));
// Update it once more to check new transactions are mined OK
simpleStorageContract.set(BigInteger.valueOf(301)).send();
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(301));
// Upgrade the chain to shanghai in 120 seconds. Then try to deploy a shanghai contract
upgradeToShanghai(
minerNode1, minerNode2, minerNode3, minerNode4, Instant.now().getEpochSecond() + 120);
Thread.sleep(THREE_MINUTES);
SimpleStorageShanghai simpleStorageContractShanghai =
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorageShanghai.class));
// Check the contract address is as expected for this sender & nonce
contractVerifier
.validTransactionReceipt("0x05d91b9031a655d08e654177336d08543ac4b711")
.verify(simpleStorageContractShanghai);
}
private static void updateGenesisConfigToLondon(
final BesuNode minerNode, final boolean zeroBaseFeeEnabled, final int blockNumber) {
if (minerNode.getGenesisConfig().isPresent()) {
final ObjectNode genesisConfigNode =
JsonUtil.objectNodeFromString(minerNode.getGenesisConfig().get());
final ObjectNode config = (ObjectNode) genesisConfigNode.get("config");
config.put("londonBlock", blockNumber);
config.put("zeroBaseFee", zeroBaseFeeEnabled);
minerNode.setGenesisConfig(genesisConfigNode.toString());
}
}
private static void updateGenesisConfigToShanghai(
final BesuNode minerNode, final long blockTimestamp) {
if (minerNode.getGenesisConfig().isPresent()) {
final ObjectNode genesisConfigNode =
JsonUtil.objectNodeFromString(minerNode.getGenesisConfig().get());
final ObjectNode config = (ObjectNode) genesisConfigNode.get("config");
config.put("shanghaiTime", blockTimestamp);
minerNode.setGenesisConfig(genesisConfigNode.toString());
}
}
private void upgradeToLondon(
final BesuNode minerNode1,
final BesuNode minerNode2,
final BesuNode minerNode3,
final BesuNode minerNode4,
final int londonBlockNumber)
throws InterruptedException {
// Node 1
stopNode(minerNode1);
updateGenesisConfigToLondon(minerNode1, true, londonBlockNumber);
startNode(minerNode1);
// Node 2
stopNode(minerNode2);
updateGenesisConfigToLondon(minerNode2, true, londonBlockNumber);
startNode(minerNode2);
// Node 3
stopNode(minerNode3);
updateGenesisConfigToLondon(minerNode3, true, londonBlockNumber);
startNode(minerNode3);
// Node 4
stopNode(minerNode4);
updateGenesisConfigToLondon(minerNode4, true, londonBlockNumber);
startNode(minerNode4);
}
private void upgradeToShanghai(
final BesuNode minerNode1,
final BesuNode minerNode2,
final BesuNode minerNode3,
final BesuNode minerNode4,
final long shanghaiTime)
throws InterruptedException {
// Node 1
stopNode(minerNode1);
updateGenesisConfigToShanghai(minerNode1, shanghaiTime);
startNode(minerNode1);
// Node 2
stopNode(minerNode2);
updateGenesisConfigToShanghai(minerNode2, shanghaiTime);
startNode(minerNode2);
// Node 3
stopNode(minerNode3);
updateGenesisConfigToShanghai(minerNode3, shanghaiTime);
startNode(minerNode3);
// Node 4
stopNode(minerNode4);
updateGenesisConfigToShanghai(minerNode4, shanghaiTime);
startNode(minerNode4);
}
// Start a node with a delay before returning to give it time to start
private void startNode(final BesuNode node) throws InterruptedException {
cluster.startNode(node);
Thread.sleep(TEN_SECONDS);
}
// Stop a node with a delay before returning to give it time to stop
private void stopNode(final BesuNode node) throws InterruptedException {
cluster.stopNode(node);
Thread.sleep(TEN_SECONDS);
}
@Override
public void tearDownAcceptanceTestBase() {
cluster.stop();
super.tearDownAcceptanceTestBase();
}
}

@ -386,7 +386,7 @@ task checkMavenCoordinateCollisions {
getAllprojects().forEach {
if (it.properties.containsKey('publishing') && it.jar?.enabled) {
def coordinate = it.publishing?.publications[0].coordinates
if (coordinates.containsKey(coordinate)) {
if (coordinate.toString().startsWith("org") && coordinates.containsKey(coordinate)) {
throw new GradleException("Duplicate maven coordinates detected, ${coordinate} is used by " +
"both ${coordinates[coordinate]} and ${it.path}.\n" +
"Please add a `publishing` script block to one or both subprojects.")

@ -28,6 +28,7 @@ rootProject.name='besu'
include 'acceptance-tests:test-plugins'
include 'acceptance-tests:dsl'
include 'acceptance-tests:tests'
include 'acceptance-tests:tests:shanghai'
include 'besu'
include 'config'
include 'consensus:clique'

Loading…
Cancel
Save