diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b5f6eac341..9b64946ae01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,31 @@ # Changelog -## Next Release +## 24.6.0 ### Breaking Changes - Java 21 has been enforced as minimum version to build and run Besu. +- With --Xbonsai-limit-trie-logs-enabled by default in this release, historic trie log data will be removed from the database unless sync-mode=FULL. It respects the --bonsai-historical-block-limit setting so shouldn't break any RPCs, but may be breaking if you are accessing this data from the database directly. Can be disabled with --bonsai-limit-trie-logs-enabled=false - In profile=ENTERPRISE, use sync-mode=FULL (instead of FAST) and data-storage-format=FOREST (instead of BONSAI) [#7186](https://github.com/hyperledger/besu/pull/7186) - If this breaks your node, you can reset sync-mode=FAST and data-storage-format=BONSAI +### Upcoming Breaking Changes +- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version. +- PKI-backed QBFT will be removed in a future version of Besu. Other forms of QBFT will remain unchanged. +- --Xbonsai-limit-trie-logs-enabled is deprecated, use --bonsai-limit-trie-logs-enabled instead +- --Xbonsai-trie-logs-pruning-window-size is deprecated, use --bonsai-trie-logs-pruning-window-size instead +- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version. + ### Additions and Improvements - Add two counters to DefaultBlockchain in order to be able to calculate TPS and Mgas/s [#7105](https://github.com/hyperledger/besu/pull/7105) +- Enable --Xbonsai-limit-trie-logs-enabled by default, unless sync-mode=FULL [#7181](https://github.com/hyperledger/besu/pull/7181) +- Promote experimental --Xbonsai-limit-trie-logs-enabled to production-ready, --bonsai-limit-trie-logs-enabled [#7192](https://github.com/hyperledger/besu/pull/7192) +- Promote experimental --Xbonsai-trie-logs-pruning-window-size to production-ready, --bonsai-trie-logs-pruning-window-size [#7192](https://github.com/hyperledger/besu/pull/7192) - `admin_nodeInfo` JSON/RPC call returns the currently active EVM version [#7127](https://github.com/hyperledger/besu/pull/7127) - +- Improve the selection of the most profitable built block [#7174](https://github.com/hyperledger/besu/pull/7174) +- Support for eth_maxPriorityFeePerGas [#5658](https://github.com/hyperledger/besu/issues/5658) ### Bug fixes - Make `eth_gasPrice` aware of the base fee market [#7102](https://github.com/hyperledger/besu/pull/7102) - +- Validation errors ignored in accounts-allowlist and empty list [#7138](https://github.com/hyperledger/besu/issues/7138) ## 24.5.2 ### Upcoming Breaking Changes diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index e0be5f27b6d..73345c68d80 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -463,16 +463,30 @@ public BesuNode createIbft2Node(final String name, final String genesisFile) thr .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 { @@ -500,16 +514,31 @@ public BesuNode createQbftNodeWithTLSPKCS11(final String name) throws IOExceptio 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( diff --git a/acceptance-tests/tests/build.gradle b/acceptance-tests/tests/build.gradle index a9393b1ccf1..1bc0e55567f 100644 --- a/acceptance-tests/tests/build.gradle +++ b/acceptance-tests/tests/build.gradle @@ -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/**' diff --git a/acceptance-tests/tests/contracts/CrossContractReader.sol b/acceptance-tests/tests/contracts/CrossContractReader.sol index f43ce8b621e..9524d5bde86 100644 --- a/acceptance-tests/tests/contracts/CrossContractReader.sol +++ b/acceptance-tests/tests/contracts/CrossContractReader.sol @@ -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; diff --git a/acceptance-tests/tests/contracts/EventEmitter.sol b/acceptance-tests/tests/contracts/EventEmitter.sol index 2e6e29e59d6..05b8868eee9 100644 --- a/acceptance-tests/tests/contracts/EventEmitter.sol +++ b/acceptance-tests/tests/contracts/EventEmitter.sol @@ -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); diff --git a/acceptance-tests/tests/contracts/RemoteSimpleStorage.sol b/acceptance-tests/tests/contracts/RemoteSimpleStorage.sol index 03c95dc2bd1..f399658789f 100644 --- a/acceptance-tests/tests/contracts/RemoteSimpleStorage.sol +++ b/acceptance-tests/tests/contracts/RemoteSimpleStorage.sol @@ -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; diff --git a/acceptance-tests/tests/contracts/RevertReason.sol b/acceptance-tests/tests/contracts/RevertReason.sol index b1270fe4ccd..2d42cafe3c2 100644 --- a/acceptance-tests/tests/contracts/RevertReason.sol +++ b/acceptance-tests/tests/contracts/RevertReason.sol @@ -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) { diff --git a/acceptance-tests/tests/contracts/SimpleStorage.sol b/acceptance-tests/tests/contracts/SimpleStorage.sol index 9303def9e05..712e2a87456 100644 --- a/acceptance-tests/tests/contracts/SimpleStorage.sol +++ b/acceptance-tests/tests/contracts/SimpleStorage.sol @@ -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; diff --git a/acceptance-tests/tests/shanghai/build.gradle b/acceptance-tests/tests/shanghai/build.gradle new file mode 100644 index 00000000000..c309d9e9914 --- /dev/null +++ b/acceptance-tests/tests/shanghai/build.gradle @@ -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' +} diff --git a/acceptance-tests/tests/shanghai/shanghaicontracts/SimpleStorageShanghai.sol b/acceptance-tests/tests/shanghai/shanghaicontracts/SimpleStorageShanghai.sol new file mode 100644 index 00000000000..32a7d9a2a09 --- /dev/null +++ b/acceptance-tests/tests/shanghai/shanghaicontracts/SimpleStorageShanghai.sol @@ -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; + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/BftAcceptanceTestParameterization.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/BftAcceptanceTestParameterization.java index c9fcf364840..15872070d00 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/BftAcceptanceTestParameterization.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/BftAcceptanceTestParameterization.java @@ -38,7 +38,14 @@ public static Stream getFactories() { @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 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( diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bftsoak/BftMiningSoakTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bftsoak/BftMiningSoakTest.java new file mode 100644 index 00000000000..9861a7dab65 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bftsoak/BftMiningSoakTest.java @@ -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(); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 72eac8efdce..5627172b8a0 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1578,7 +1578,7 @@ private void validateTransactionPoolOptions() { } private void validateDataStorageOptions() { - dataStorageOptions.validate(commandLine); + dataStorageOptions.validate(commandLine, syncMode); } private void validateRequiredOptions() { @@ -2808,11 +2808,12 @@ private String generateConfigurationOverview() { builder.setHighSpecEnabled(); } - if (getDataStorageConfiguration().getUnstable().getBonsaiLimitTrieLogsEnabled()) { + if (DataStorageFormat.BONSAI.equals(getDataStorageConfiguration().getDataStorageFormat()) + && getDataStorageConfiguration().getBonsaiLimitTrieLogsEnabled()) { builder.setLimitTrieLogsEnabled(); builder.setTrieLogRetentionLimit(getDataStorageConfiguration().getBonsaiMaxLayersToLoad()); builder.setTrieLogsPruningWindowSize( - getDataStorageConfiguration().getUnstable().getBonsaiTrieLogPruningWindowSize()); + getDataStorageConfiguration().getBonsaiTrieLogPruningWindowSize()); } builder.setSnapServerEnabled(this.unstableSynchronizerOptions.isSnapsyncServerEnabled()); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java index 51fb4d3ade1..d1cb4f721b2 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java @@ -14,16 +14,17 @@ */ package org.hyperledger.besu.cli.options.stable; +import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; +import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_RECEIPT_COMPACTION_ENABLED; +import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_FULL_FLAT_DB_ENABLED; -import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; -import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; -import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT; import org.hyperledger.besu.cli.options.CLIOptions; import org.hyperledger.besu.cli.util.CommandLineUtils; +import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; @@ -57,11 +58,35 @@ public class DataStorageOptions implements CLIOptions paramLabel = "", description = "Limit of historical layers that can be loaded with BONSAI (default: ${DEFAULT-VALUE}). When using " - + Unstable.BONSAI_LIMIT_TRIE_LOGS_ENABLED + + BONSAI_LIMIT_TRIE_LOGS_ENABLED + " it will also be used as the number of layers of trie logs to retain.", arity = "1") private Long bonsaiMaxLayersToLoad = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; + private static final String BONSAI_LIMIT_TRIE_LOGS_ENABLED = "--bonsai-limit-trie-logs-enabled"; + + /** The bonsai trie logs pruning window size. */ + public static final String BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = + "--bonsai-trie-logs-pruning-window-size"; + + @SuppressWarnings("ExperimentalCliOptionMustBeCorrectlyDisplayed") + @CommandLine.Option( + names = { + BONSAI_LIMIT_TRIE_LOGS_ENABLED, + "--Xbonsai-limit-trie-logs-enabled", + "--Xbonsai-trie-log-pruning-enabled" + }, + fallbackValue = "true", + description = "Limit the number of trie logs that are retained. (default: ${DEFAULT-VALUE})") + private Boolean bonsaiLimitTrieLogsEnabled = DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; + + @SuppressWarnings("ExperimentalCliOptionMustBeCorrectlyDisplayed") + @CommandLine.Option( + names = {BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE, "--Xbonsai-trie-logs-pruning-window-size"}, + description = + "The max number of blocks to load and prune trie logs for at startup. (default: ${DEFAULT-VALUE})") + private Integer bonsaiTrieLogPruningWindowSize = DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; + @Option( names = "--receipt-compaction-enabled", description = "Enables compact storing of receipts (default: ${DEFAULT-VALUE}).", @@ -76,26 +101,6 @@ public class DataStorageOptions implements CLIOptions /** The unstable options for data storage. */ public static class Unstable { - private static final String BONSAI_LIMIT_TRIE_LOGS_ENABLED = - "--Xbonsai-limit-trie-logs-enabled"; - - /** The bonsai trie logs pruning window size. */ - public static final String BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = - "--Xbonsai-trie-logs-pruning-window-size"; - - @CommandLine.Option( - hidden = true, - names = {BONSAI_LIMIT_TRIE_LOGS_ENABLED, "--Xbonsai-trie-log-pruning-enabled"}, - description = - "Limit the number of trie logs that are retained. (default: ${DEFAULT-VALUE})") - private boolean bonsaiLimitTrieLogsEnabled = DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; - - @CommandLine.Option( - hidden = true, - names = {BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE}, - description = - "The max number of blocks to load and prune trie logs for at startup. (default: ${DEFAULT-VALUE})") - private int bonsaiTrieLogPruningWindowSize = DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; // TODO: --Xsnapsync-synchronizer-flat-db-healing-enabled is deprecated, remove it in a future // release @@ -134,9 +139,17 @@ public static DataStorageOptions create() { * Validates the data storage options * * @param commandLine the full commandLine to check all the options specified by the user + * @param syncMode the sync mode */ - public void validate(final CommandLine commandLine) { - if (unstableOptions.bonsaiLimitTrieLogsEnabled) { + public void validate(final CommandLine commandLine, final SyncMode syncMode) { + if (DataStorageFormat.BONSAI == dataStorageFormat && bonsaiLimitTrieLogsEnabled) { + if (SyncMode.FULL == syncMode) { + throw new CommandLine.ParameterException( + commandLine, + String.format( + "Cannot enable " + BONSAI_LIMIT_TRIE_LOGS_ENABLED + " with sync-mode %s", + syncMode)); + } if (bonsaiMaxLayersToLoad < MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT) { throw new CommandLine.ParameterException( commandLine, @@ -144,22 +157,22 @@ public void validate(final CommandLine commandLine) { BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + " minimum value is %d", MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT)); } - if (unstableOptions.bonsaiTrieLogPruningWindowSize <= 0) { + if (bonsaiTrieLogPruningWindowSize <= 0) { throw new CommandLine.ParameterException( commandLine, String.format( - Unstable.BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than 0", - unstableOptions.bonsaiTrieLogPruningWindowSize)); + BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than 0", + bonsaiTrieLogPruningWindowSize)); } - if (unstableOptions.bonsaiTrieLogPruningWindowSize <= bonsaiMaxLayersToLoad) { + if (bonsaiTrieLogPruningWindowSize <= bonsaiMaxLayersToLoad) { throw new CommandLine.ParameterException( commandLine, String.format( - Unstable.BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than " + BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + "=%d", - unstableOptions.bonsaiTrieLogPruningWindowSize, + bonsaiTrieLogPruningWindowSize, bonsaiMaxLayersToLoad)); } } @@ -176,10 +189,9 @@ public static DataStorageOptions fromConfig(final DataStorageConfiguration domai dataStorageOptions.dataStorageFormat = domainObject.getDataStorageFormat(); dataStorageOptions.bonsaiMaxLayersToLoad = domainObject.getBonsaiMaxLayersToLoad(); dataStorageOptions.receiptCompactionEnabled = domainObject.getReceiptCompactionEnabled(); - dataStorageOptions.unstableOptions.bonsaiLimitTrieLogsEnabled = - domainObject.getUnstable().getBonsaiLimitTrieLogsEnabled(); - dataStorageOptions.unstableOptions.bonsaiTrieLogPruningWindowSize = - domainObject.getUnstable().getBonsaiTrieLogPruningWindowSize(); + dataStorageOptions.bonsaiLimitTrieLogsEnabled = domainObject.getBonsaiLimitTrieLogsEnabled(); + dataStorageOptions.bonsaiTrieLogPruningWindowSize = + domainObject.getBonsaiTrieLogPruningWindowSize(); dataStorageOptions.unstableOptions.bonsaiFullFlatDbEnabled = domainObject.getUnstable().getBonsaiFullFlatDbEnabled(); dataStorageOptions.unstableOptions.bonsaiCodeUsingCodeHashEnabled = @@ -194,10 +206,10 @@ public DataStorageConfiguration toDomainObject() { .dataStorageFormat(dataStorageFormat) .bonsaiMaxLayersToLoad(bonsaiMaxLayersToLoad) .receiptCompactionEnabled(receiptCompactionEnabled) + .bonsaiLimitTrieLogsEnabled(bonsaiLimitTrieLogsEnabled) + .bonsaiTrieLogPruningWindowSize(bonsaiTrieLogPruningWindowSize) .unstable( ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(unstableOptions.bonsaiLimitTrieLogsEnabled) - .bonsaiTrieLogPruningWindowSize(unstableOptions.bonsaiTrieLogPruningWindowSize) .bonsaiFullFlatDbEnabled(unstableOptions.bonsaiFullFlatDbEnabled) .bonsaiCodeStoredByCodeHashEnabled(unstableOptions.bonsaiCodeUsingCodeHashEnabled) .build()) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java index 62d316366f8..6bcfe4d3531 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java @@ -17,7 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static org.hyperledger.besu.cli.options.stable.DataStorageOptions.BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD; import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; -import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; +import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; import org.hyperledger.besu.cli.options.stable.DataStorageOptions; import org.hyperledger.besu.datatypes.Hash; @@ -296,25 +296,23 @@ private void processTransactionChunk( void validatePruneConfiguration(final DataStorageConfiguration config) { checkArgument( config.getBonsaiMaxLayersToLoad() - >= DataStorageConfiguration.Unstable.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT, + >= DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT, String.format( BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + " minimum value is %d", - DataStorageConfiguration.Unstable.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT)); + DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT)); checkArgument( - config.getUnstable().getBonsaiTrieLogPruningWindowSize() > 0, + config.getBonsaiTrieLogPruningWindowSize() > 0, String.format( - DataStorageOptions.Unstable.BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE - + "=%d must be greater than 0", - config.getUnstable().getBonsaiTrieLogPruningWindowSize())); + DataStorageOptions.BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than 0", + config.getBonsaiTrieLogPruningWindowSize())); checkArgument( - config.getUnstable().getBonsaiTrieLogPruningWindowSize() - > config.getBonsaiMaxLayersToLoad(), + config.getBonsaiTrieLogPruningWindowSize() > config.getBonsaiMaxLayersToLoad(), String.format( - DataStorageOptions.Unstable.BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + DataStorageOptions.BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE + "=%d must be greater than " + BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD + "=%d", - config.getUnstable().getBonsaiTrieLogPruningWindowSize(), + config.getBonsaiTrieLogPruningWindowSize(), config.getBonsaiMaxLayersToLoad())); } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 322f596e160..4e3929e8ae5 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -733,7 +733,7 @@ public BesuController build() { final JsonRpcMethods additionalJsonRpcMethodFactory = createAdditionalJsonRpcMethodFactory(protocolContext, protocolSchedule, miningParameters); - if (dataStorageConfiguration.getUnstable().getBonsaiLimitTrieLogsEnabled() + if (dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled() && DataStorageFormat.BONSAI.equals(dataStorageConfiguration.getDataStorageFormat())) { final TrieLogManager trieLogManager = ((BonsaiWorldStateProvider) worldStateArchive).getTrieLogManager(); @@ -784,7 +784,7 @@ private TrieLogPruner createTrieLogPruner( blockchain, scheduler::executeServiceTask, dataStorageConfiguration.getBonsaiMaxLayersToLoad(), - dataStorageConfiguration.getUnstable().getBonsaiTrieLogPruningWindowSize(), + dataStorageConfiguration.getBonsaiTrieLogPruningWindowSize(), isProofOfStake); trieLogPruner.initialize(); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 74579a975d2..e86e500a7d0 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -1087,8 +1087,8 @@ public void syncMode_fast() { } @Test - public void syncMode_full() { - parseCommand("--sync-mode", "FULL"); + public void syncMode_full_requires_bonsaiLimitTrieLogsToBeDisabled() { + parseCommand("--sync-mode", "FULL", "--bonsai-limit-trie-logs-enabled=false"); verify(mockControllerBuilder).synchronizerConfiguration(syncConfigurationCaptor.capture()); final SynchronizerConfiguration syncConfig = syncConfigurationCaptor.getValue(); @@ -1244,8 +1244,37 @@ public void ethStatsContactOptionCannotBeUsedWithoutEthStatsServerProvided() { } @Test - public void parsesValidBonsaiTrieLimitBackLayersOption() { - parseCommand("--data-storage-format", "BONSAI", "--bonsai-historical-block-limit", "11"); + public void bonsaiLimitTrieLogsEnabledByDefault() { + parseCommand(); + verify(mockControllerBuilder) + .dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture()); + + final DataStorageConfiguration dataStorageConfiguration = + dataStorageConfigurationArgumentCaptor.getValue(); + assertThat(dataStorageConfiguration.getDataStorageFormat()).isEqualTo(BONSAI); + assertThat(dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled()).isTrue(); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + } + + @Test + public void parsesInvalidDefaultBonsaiLimitTrieLogsWhenFullSyncEnabled() { + parseCommand("--sync-mode=FULL"); + + Mockito.verifyNoInteractions(mockRunnerBuilder); + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains("Cannot enable --bonsai-limit-trie-logs-enabled with sync-mode FULL"); + } + + @Test + public void parsesValidBonsaiHistoricalBlockLimitOption() { + parseCommand( + "--bonsai-limit-trie-logs-enabled=false", + "--data-storage-format", + "BONSAI", + "--bonsai-historical-block-limit", + "11"); verify(mockControllerBuilder) .dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture()); @@ -1258,7 +1287,7 @@ public void parsesValidBonsaiTrieLimitBackLayersOption() { } @Test - public void parsesInvalidBonsaiTrieLimitBackLayersOption() { + public void parsesInvalidBonsaiHistoricalBlockLimitOption() { parseCommand("--data-storage-format", "BONSAI", "--bonsai-maximum-back-layers-to-load", "ten"); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java index 4508f6c4e59..2086381825f 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java @@ -15,7 +15,7 @@ package org.hyperledger.besu.cli.options.stable; import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT; +import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT; import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; @@ -31,28 +31,45 @@ public class DataStorageOptionsTest public void bonsaiTrieLogPruningLimitOption() { internalTestSuccess( dataStorageConfiguration -> - assertThat(dataStorageConfiguration.getUnstable().getBonsaiTrieLogPruningWindowSize()) - .isEqualTo(600), + assertThat(dataStorageConfiguration.getBonsaiTrieLogPruningWindowSize()).isEqualTo(600), + "--bonsai-limit-trie-logs-enabled", + "--bonsai-trie-logs-pruning-window-size", + "600"); + } + + @Test + public void bonsaiTrieLogPruningLimitLegacyOption() { + internalTestSuccess( + dataStorageConfiguration -> + assertThat(dataStorageConfiguration.getBonsaiTrieLogPruningWindowSize()).isEqualTo(600), "--Xbonsai-limit-trie-logs-enabled", "--Xbonsai-trie-logs-pruning-window-size", "600"); } + @Test + public void bonsaiTrieLogsEnabled_explicitlySetToFalse() { + internalTestSuccess( + dataStorageConfiguration -> + assertThat(dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled()).isEqualTo(false), + "--bonsai-limit-trie-logs-enabled=false"); + } + @Test public void bonsaiTrieLogPruningWindowSizeShouldBePositive() { internalTestFailure( - "--Xbonsai-trie-logs-pruning-window-size=0 must be greater than 0", - "--Xbonsai-limit-trie-logs-enabled", - "--Xbonsai-trie-logs-pruning-window-size", + "--bonsai-trie-logs-pruning-window-size=0 must be greater than 0", + "--bonsai-limit-trie-logs-enabled", + "--bonsai-trie-logs-pruning-window-size", "0"); } @Test public void bonsaiTrieLogPruningWindowSizeShouldBeAboveRetentionLimit() { internalTestFailure( - "--Xbonsai-trie-logs-pruning-window-size=512 must be greater than --bonsai-historical-block-limit=512", - "--Xbonsai-limit-trie-logs-enabled", - "--Xbonsai-trie-logs-pruning-window-size", + "--bonsai-trie-logs-pruning-window-size=512 must be greater than --bonsai-historical-block-limit=512", + "--bonsai-limit-trie-logs-enabled", + "--bonsai-trie-logs-pruning-window-size", "512"); } @@ -62,7 +79,7 @@ public void bonsaiTrieLogRetentionLimitOption() { dataStorageConfiguration -> assertThat(dataStorageConfiguration.getBonsaiMaxLayersToLoad()) .isEqualTo(MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT + 1), - "--Xbonsai-limit-trie-logs-enabled", + "--bonsai-limit-trie-logs-enabled", "--bonsai-historical-block-limit", "513"); } @@ -73,7 +90,7 @@ public void bonsaiTrieLogRetentionLimitOption_boundaryTest() { dataStorageConfiguration -> assertThat(dataStorageConfiguration.getBonsaiMaxLayersToLoad()) .isEqualTo(MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT), - "--Xbonsai-limit-trie-logs-enabled", + "--bonsai-limit-trie-logs-enabled", "--bonsai-historical-block-limit", "512"); } @@ -82,7 +99,7 @@ public void bonsaiTrieLogRetentionLimitOption_boundaryTest() { public void bonsaiTrieLogRetentionLimitShouldBeAboveMinimum() { internalTestFailure( "--bonsai-historical-block-limit minimum value is 512", - "--Xbonsai-limit-trie-logs-enabled", + "--bonsai-limit-trie-logs-enabled", "--bonsai-historical-block-limit", "511"); } @@ -137,11 +154,8 @@ protected DataStorageConfiguration createCustomizedDomainObject() { return ImmutableDataStorageConfiguration.builder() .dataStorageFormat(DataStorageFormat.BONSAI) .bonsaiMaxLayersToLoad(513L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .bonsaiTrieLogPruningWindowSize(514) - .build()) + .bonsaiLimitTrieLogsEnabled(true) + .bonsaiTrieLogPruningWindowSize(514) .build(); } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelperTest.java b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelperTest.java index f8fba8d7002..2f3a693f3de 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelperTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelperTest.java @@ -17,7 +17,7 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; +import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; import static org.hyperledger.besu.plugin.services.storage.DataStorageFormat.BONSAI; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; @@ -135,10 +135,7 @@ public void prune(final @TempDir Path dataDir) throws IOException { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(3L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); mockBlockchainBase(); @@ -176,10 +173,7 @@ public void cannotPruneIfNoFinalizedIsFound() { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(2L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); when(blockchain.getChainHeadBlockNumber()).thenReturn(5L); @@ -199,10 +193,7 @@ public void cannotPruneIfUserRetainsMoreLayersThanExistingChainLength() { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(10L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); when(blockchain.getChainHeadBlockNumber()).thenReturn(5L); @@ -222,10 +213,7 @@ public void cannotPruneIfUserRequiredFurtherThanFinalized(final @TempDir Path da ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(2L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); mockBlockchainBase(); @@ -246,10 +234,7 @@ public void skipPruningIfTrieLogCountIsLessThanMaxLayersToLoad() { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(6L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); when(blockchain.getChainHeadBlockNumber()).thenReturn(5L); @@ -271,10 +256,7 @@ public void mismatchInPrunedTrieLogCountShouldNotDeleteFiles(final @TempDir Path ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(3L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); mockBlockchainBase(); @@ -303,10 +285,7 @@ public void trieLogRetentionLimitShouldBeAboveMinimum() { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(511L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); TrieLogHelper helper = new TrieLogHelper(); @@ -324,11 +303,8 @@ public void trieLogPruningWindowSizeShouldBePositive() { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(512L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .bonsaiTrieLogPruningWindowSize(0) - .build()) + .bonsaiLimitTrieLogsEnabled(true) + .bonsaiTrieLogPruningWindowSize(0) .build(); TrieLogHelper helper = new TrieLogHelper(); @@ -336,7 +312,7 @@ public void trieLogPruningWindowSizeShouldBePositive() { () -> helper.prune(dataStorageConfiguration, inMemoryWorldState, blockchain, Path.of(""))) .isInstanceOf(RuntimeException.class) - .hasMessage("--Xbonsai-trie-logs-pruning-window-size=0 must be greater than 0"); + .hasMessage("--bonsai-trie-logs-pruning-window-size=0 must be greater than 0"); } @Test @@ -345,11 +321,8 @@ public void trieLogPruningWindowSizeShouldBeAboveRetentionLimit() { ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(512L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .bonsaiTrieLogPruningWindowSize(512) - .build()) + .bonsaiLimitTrieLogsEnabled(true) + .bonsaiTrieLogPruningWindowSize(512) .build(); TrieLogHelper helper = new TrieLogHelper(); @@ -358,7 +331,7 @@ public void trieLogPruningWindowSizeShouldBeAboveRetentionLimit() { helper.prune(dataStorageConfiguration, inMemoryWorldState, blockchain, Path.of(""))) .isInstanceOf(RuntimeException.class) .hasMessage( - "--Xbonsai-trie-logs-pruning-window-size=512 must be greater than --bonsai-historical-block-limit=512"); + "--bonsai-trie-logs-pruning-window-size=512 must be greater than --bonsai-historical-block-limit=512"); } @Test @@ -368,10 +341,7 @@ public void exceptionWhileSavingFileStopsPruneProcess(final @TempDir Path dataDi ImmutableDataStorageConfiguration.builder() .dataStorageFormat(BONSAI) .bonsaiMaxLayersToLoad(3L) - .unstable( - ImmutableDataStorageConfiguration.Unstable.builder() - .bonsaiLimitTrieLogsEnabled(true) - .build()) + .bonsaiLimitTrieLogsEnabled(true) .build(); mockBlockchainBase(); diff --git a/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java b/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java index f7db3639ae1..1e734f62312 100644 --- a/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java +++ b/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java @@ -125,7 +125,7 @@ void shouldRetrieveStateUpdatePostTracingForOneBlock() { verify(opTracer).traceStartTransaction(any(), eq(tx)); verify(opTracer) .traceEndTransaction( - any(), eq(tx), anyBoolean(), any(), any(), anyLong(), anyLong()); + any(), eq(tx), anyBoolean(), any(), any(), anyLong(), any(), anyLong()); }); verify(opTracer).traceEndBlock(tracedBlock.getHeader(), tracedBlock.getBody()); @@ -173,7 +173,14 @@ void shouldRetrieveStateUpdatePostTracingForAllBlocks() { verify(opTracer).traceStartTransaction(any(), eq(tx)); verify(opTracer) .traceEndTransaction( - any(), eq(tx), anyBoolean(), any(), any(), anyLong(), anyLong()); + any(), + eq(tx), + anyBoolean(), + any(), + any(), + anyLong(), + any(), + anyLong()); }); verify(opTracer).traceEndBlock(tracedBlock.getHeader(), tracedBlock.getBody()); @@ -222,6 +229,7 @@ void shouldReturnTheCorrectWorldViewForTxStartEnd() { assertThat(txStartEndTracer.txEndStatus).isTrue(); assertThat(txStartEndTracer.txEndOutput).isEqualTo(Bytes.fromHexString("0x")); assertThat(txStartEndTracer.txEndGasUsed).isEqualTo(24303); + assertThat(txStartEndTracer.txEndSelfDestructs).isEmpty(); assertThat(txStartEndTracer.txEndTimeNs).isNotNull(); assertThat(txStartEndTracer.txEndLogs).isNotEmpty(); @@ -263,6 +271,7 @@ private static class TxStartEndTracer implements BlockAwareOperationTracer { public Bytes txEndOutput; public List txEndLogs; public long txEndGasUsed; + public Set
txEndSelfDestructs; public Long txEndTimeNs; private final Set traceStartTxCalled = new HashSet<>(); @@ -287,6 +296,7 @@ public void traceEndTransaction( final Bytes output, final List logs, final long gasUsed, + final Set
selfDestructs, final long timeNs) { if (!traceEndTxCalled.add(transaction)) { fail("traceEndTransaction already called for tx " + transaction); @@ -297,6 +307,7 @@ public void traceEndTransaction( txEndOutput = output; txEndLogs = logs; txEndGasUsed = gasUsed; + txEndSelfDestructs = selfDestructs; txEndTimeNs = timeNs; } diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index d1cdaa32463..6c98e2ebaf7 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -213,6 +213,8 @@ ethstats-cacert-file="./root.cert" # Data storage data-storage-format="BONSAI" bonsai-historical-block-limit=512 +bonsai-limit-trie-logs-enabled=true +bonsai-trie-logs-pruning-window-size=100_000 receipt-compaction-enabled=true # feature flags diff --git a/build.gradle b/build.gradle index b7913258cc7..c2cc8514ba5 100644 --- a/build.gradle +++ b/build.gradle @@ -373,7 +373,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.") diff --git a/config/src/main/resources/profiles/enterprise-private.toml b/config/src/main/resources/profiles/enterprise-private.toml index 9ae88a55f27..ea87d5cfc18 100644 --- a/config/src/main/resources/profiles/enterprise-private.toml +++ b/config/src/main/resources/profiles/enterprise-private.toml @@ -6,4 +6,5 @@ tx-pool="SEQUENCED" tx-pool-no-local-priority=true tx-pool-limit-by-account-percentage=0.15 rpc-http-max-active-connections=300 -min-gas-price=0 \ No newline at end of file +min-gas-price=0 +bonsai-limit-trie-logs-enabled=false \ No newline at end of file diff --git a/config/src/main/resources/profiles/minimalist-staker.toml b/config/src/main/resources/profiles/minimalist-staker.toml index fcdbf64363f..cfda132e374 100644 --- a/config/src/main/resources/profiles/minimalist-staker.toml +++ b/config/src/main/resources/profiles/minimalist-staker.toml @@ -1,4 +1,3 @@ sync-mode="CHECKPOINT" data-storage-format="BONSAI" -bonsai-historical-block-limit=128 max-peers=25 \ No newline at end of file diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeContext.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeContext.java index 64560df05c6..2554a4537fa 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeContext.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeContext.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.ConsensusContext; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -167,7 +166,7 @@ void fireNewUnverifiedForkchoiceEvent( * @param payloadId the payload identifier * @return the optional block with receipts */ - Optional retrieveBlockById(final PayloadIdentifier payloadId); + Optional retrievePayloadById(final PayloadIdentifier payloadId); /** * Is configured for a post-merge from genesis. diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PayloadWrapper.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PayloadWrapper.java index 35df16c8073..4a8588f84ef 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PayloadWrapper.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PayloadWrapper.java @@ -15,13 +15,53 @@ package org.hyperledger.besu.consensus.merge; import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockValueCalculator; import org.hyperledger.besu.ethereum.core.BlockWithReceipts; -/** - * Wrapper for payload plus extra info. - * - * @param payloadIdentifier Payload identifier - * @param blockWithReceipts Block With Receipts - */ -public record PayloadWrapper( - PayloadIdentifier payloadIdentifier, BlockWithReceipts blockWithReceipts) {} +/** Wrapper for payload plus extra info. */ +public class PayloadWrapper { + private final PayloadIdentifier payloadIdentifier; + private final BlockWithReceipts blockWithReceipts; + private final Wei blockValue; + + /** + * Construct a wrapper with the following fields. + * + * @param payloadIdentifier Payload identifier + * @param blockWithReceipts Block with receipts + */ + public PayloadWrapper( + final PayloadIdentifier payloadIdentifier, final BlockWithReceipts blockWithReceipts) { + this.blockWithReceipts = blockWithReceipts; + this.payloadIdentifier = payloadIdentifier; + this.blockValue = BlockValueCalculator.calculateBlockValue(blockWithReceipts); + } + + /** + * Get the block value + * + * @return block value in Wei + */ + public Wei blockValue() { + return blockValue; + } + + /** + * Get this payload identifier + * + * @return payload identifier + */ + public PayloadIdentifier payloadIdentifier() { + return payloadIdentifier; + } + + /** + * Get the block with receipts + * + * @return block with receipts + */ + public BlockWithReceipts blockWithReceipts() { + return blockWithReceipts; + } +} diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PostMergeContext.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PostMergeContext.java index d296d8cdf3b..1d7e4b76c62 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PostMergeContext.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/PostMergeContext.java @@ -16,12 +16,9 @@ import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.ConsensusContext; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.BlockValueCalculator; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.util.Subscribers; @@ -29,7 +26,6 @@ import java.util.Comparator; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.common.annotations.VisibleForTesting; @@ -45,13 +41,6 @@ public class PostMergeContext implements MergeContext { static final int MAX_BLOCKS_IN_PROGRESS = 12; private static final AtomicReference singleton = new AtomicReference<>(); - - private static final Comparator compareByGasUsedDesc = - Comparator.comparingLong( - (BlockWithReceipts blockWithReceipts) -> - blockWithReceipts.getBlock().getHeader().getGasUsed()) - .reversed(); - private final AtomicReference syncState; private final AtomicReference terminalTotalDifficulty; // initial postMerge state is indeterminate until it is set: @@ -70,7 +59,6 @@ public class PostMergeContext implements MergeContext { private final AtomicReference lastSafeBlock = new AtomicReference<>(); private final AtomicReference> terminalPoWBlock = new AtomicReference<>(Optional.empty()); - private final BlockValueCalculator blockValueCalculator = new BlockValueCalculator(); private boolean isPostMergeAtGenesis; /** Instantiates a new Post merge context. */ @@ -227,66 +215,65 @@ public boolean validateCandidateHead(final BlockHeader candidateHeader) { } @Override - public void putPayloadById(final PayloadWrapper payloadWrapper) { + public void putPayloadById(final PayloadWrapper newPayload) { + final var newBlockWithReceipts = newPayload.blockWithReceipts(); + final var newBlockValue = newPayload.blockValue(); + synchronized (blocksInProgress) { - final Optional maybeCurrBestBlock = - retrieveBlockById(payloadWrapper.payloadIdentifier()); + final Optional maybeCurrBestPayload = + retrievePayloadById(newPayload.payloadIdentifier()); - maybeCurrBestBlock.ifPresentOrElse( - currBestBlock -> { - if (compareByGasUsedDesc.compare(payloadWrapper.blockWithReceipts(), currBestBlock) - < 0) { + maybeCurrBestPayload.ifPresent( + currBestPayload -> { + if (newBlockValue.greaterThan(currBestPayload.blockValue())) { LOG.atDebug() - .setMessage("New proposal for payloadId {} {} is better than the previous one {}") - .addArgument(payloadWrapper.payloadIdentifier()) + .setMessage( + "New proposal for payloadId {} {} is better than the previous one {} by {}") + .addArgument(newPayload.payloadIdentifier()) + .addArgument(() -> logBlockProposal(newBlockWithReceipts.getBlock())) .addArgument( - () -> logBlockProposal(payloadWrapper.blockWithReceipts().getBlock())) - .addArgument(() -> logBlockProposal(currBestBlock.getBlock())) + () -> logBlockProposal(currBestPayload.blockWithReceipts().getBlock())) + .addArgument( + () -> + newBlockValue + .subtract(currBestPayload.blockValue()) + .toHumanReadableString()) .log(); + blocksInProgress.removeAll( - retrievePayloadsById(payloadWrapper.payloadIdentifier()) - .collect(Collectors.toUnmodifiableList())); - blocksInProgress.add( - new PayloadWrapper( - payloadWrapper.payloadIdentifier(), payloadWrapper.blockWithReceipts())); - logCurrentBestBlock(payloadWrapper.blockWithReceipts()); + streamPayloadsById(newPayload.payloadIdentifier()).toList()); + + logCurrentBestBlock(newPayload); } - }, - () -> - blocksInProgress.add( - new PayloadWrapper( - payloadWrapper.payloadIdentifier(), payloadWrapper.blockWithReceipts()))); + }); + blocksInProgress.add(newPayload); } } - private void logCurrentBestBlock(final BlockWithReceipts blockWithReceipts) { + private void logCurrentBestBlock(final PayloadWrapper payloadWrapper) { if (LOG.isDebugEnabled()) { - final Block block = blockWithReceipts.getBlock(); + final Block block = payloadWrapper.blockWithReceipts().getBlock(); final float gasUsedPerc = 100.0f * block.getHeader().getGasUsed() / block.getHeader().getGasLimit(); final int txsNum = block.getBody().getTransactions().size(); - final Wei reward = blockValueCalculator.calculateBlockValue(blockWithReceipts); LOG.debug( "Current best proposal for block {}: txs {}, gas used {}%, reward {}", - blockWithReceipts.getNumber(), + block.getHeader().getNumber(), txsNum, String.format("%1.2f", gasUsedPerc), - reward.toHumanReadableString()); + payloadWrapper.blockValue().toHumanReadableString()); } } @Override - public Optional retrieveBlockById(final PayloadIdentifier payloadId) { + public Optional retrievePayloadById(final PayloadIdentifier payloadId) { synchronized (blocksInProgress) { - return retrievePayloadsById(payloadId) - .map(payloadWrapper -> payloadWrapper.blockWithReceipts()) - .sorted(compareByGasUsedDesc) - .findFirst(); + return streamPayloadsById(payloadId).max(Comparator.comparing(PayloadWrapper::blockValue)); } } - private Stream retrievePayloadsById(final PayloadIdentifier payloadId) { + private Stream streamPayloadsById(final PayloadIdentifier payloadId) { return blocksInProgress.stream().filter(z -> z.payloadIdentifier().equals(payloadId)); } diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionContext.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionContext.java index ea003c7090a..9d8c927e1ee 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionContext.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionContext.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.ConsensusContext; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -146,8 +145,8 @@ public void putPayloadById(final PayloadWrapper payloadWrapper) { } @Override - public Optional retrieveBlockById(final PayloadIdentifier payloadId) { - return postMergeContext.retrieveBlockById(payloadId); + public Optional retrievePayloadById(final PayloadIdentifier payloadId) { + return postMergeContext.retrievePayloadById(payloadId); } @Override diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/PostMergeContextTest.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/PostMergeContextTest.java index 91086a8d7c4..6abf09bde0f 100644 --- a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/PostMergeContextTest.java +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/PostMergeContextTest.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockWithReceipts; @@ -138,9 +139,12 @@ public void putAndRetrieveFirstPayload() { BlockWithReceipts mockBlockWithReceipts = createBlockWithReceipts(1, 21000, 1); PayloadIdentifier firstPayloadId = new PayloadIdentifier(1L); - postMergeContext.putPayloadById(new PayloadWrapper(firstPayloadId, mockBlockWithReceipts)); + final var payloadWrapper = createPayloadWrapper(firstPayloadId, mockBlockWithReceipts, Wei.ONE); + postMergeContext.putPayloadById(payloadWrapper); - assertThat(postMergeContext.retrieveBlockById(firstPayloadId)).contains(mockBlockWithReceipts); + assertThat(postMergeContext.retrievePayloadById(firstPayloadId)) + .map(PayloadWrapper::blockWithReceipts) + .contains(mockBlockWithReceipts); } @Test @@ -149,10 +153,16 @@ public void puttingTwoBlocksWithTheSamePayloadIdWeRetrieveTheBest() { BlockWithReceipts betterBlockWithReceipts = createBlockWithReceipts(2, 11, 1); PayloadIdentifier payloadId = new PayloadIdentifier(1L); - postMergeContext.putPayloadById(new PayloadWrapper(payloadId, zeroTxBlockWithReceipts)); - postMergeContext.putPayloadById(new PayloadWrapper(payloadId, betterBlockWithReceipts)); - - assertThat(postMergeContext.retrieveBlockById(payloadId)).contains(betterBlockWithReceipts); + final var zeroTxPayloadWrapper = + createPayloadWrapper(payloadId, zeroTxBlockWithReceipts, Wei.ZERO); + final var betterPayloadWrapper = + createPayloadWrapper(payloadId, betterBlockWithReceipts, Wei.ONE); + postMergeContext.putPayloadById(zeroTxPayloadWrapper); + postMergeContext.putPayloadById(betterPayloadWrapper); + + assertThat(postMergeContext.retrievePayloadById(payloadId)) + .map(PayloadWrapper::blockWithReceipts) + .contains(betterBlockWithReceipts); } @Test @@ -162,25 +172,33 @@ public void puttingABlockWithTheSamePayloadIdSmallerThanAnExistingOneWeRetrieveT BlockWithReceipts smallBlockWithReceipts = createBlockWithReceipts(3, 5, 1); PayloadIdentifier payloadId = new PayloadIdentifier(1L); - postMergeContext.putPayloadById(new PayloadWrapper(payloadId, zeroTxBlockWithReceipts)); - postMergeContext.putPayloadById(new PayloadWrapper(payloadId, betterBlockWithReceipts)); - postMergeContext.putPayloadById(new PayloadWrapper(payloadId, smallBlockWithReceipts)); - - assertThat(postMergeContext.retrieveBlockById(payloadId)).contains(betterBlockWithReceipts); + final var zeroTxPayloadWrapper = + createPayloadWrapper(payloadId, zeroTxBlockWithReceipts, Wei.ZERO); + final var betterPayloadWrapper = + createPayloadWrapper(payloadId, betterBlockWithReceipts, Wei.of(2)); + final var smallPayloadWrapper = + createPayloadWrapper(payloadId, smallBlockWithReceipts, Wei.ONE); + postMergeContext.putPayloadById(zeroTxPayloadWrapper); + postMergeContext.putPayloadById(betterPayloadWrapper); + postMergeContext.putPayloadById(smallPayloadWrapper); + + assertThat(postMergeContext.retrievePayloadById(payloadId)) + .map(PayloadWrapper::blockWithReceipts) + .contains(betterBlockWithReceipts); } @Test public void tryingToRetrieveANotYetPutPayloadIdReturnsEmpty() { PayloadIdentifier payloadId = new PayloadIdentifier(1L); - assertThat(postMergeContext.retrieveBlockById(payloadId)).isEmpty(); + assertThat(postMergeContext.retrievePayloadById(payloadId)).isEmpty(); } @Test public void tryingToRetrieveABlockPutButEvictedReturnsEmpty() { PayloadIdentifier evictedPayloadId = new PayloadIdentifier(0L); - assertThat(postMergeContext.retrieveBlockById(evictedPayloadId)).isEmpty(); + assertThat(postMergeContext.retrievePayloadById(evictedPayloadId)).isEmpty(); } @Test @@ -209,6 +227,17 @@ public void syncStateNullShouldNotThrowWhenIsSyncingIsCalled() { assertThat(postMergeContext.isSyncing()).isFalse(); } + private PayloadWrapper createPayloadWrapper( + final PayloadIdentifier firstPayloadId, + final BlockWithReceipts mockBlockWithReceipts, + final Wei blockValue) { + final var payloadWrapper = mock(PayloadWrapper.class); + when(payloadWrapper.payloadIdentifier()).thenReturn(firstPayloadId); + when(payloadWrapper.blockWithReceipts()).thenReturn(mockBlockWithReceipts); + when(payloadWrapper.blockValue()).thenReturn(blockValue); + return payloadWrapper; + } + private static BlockWithReceipts createBlockWithReceipts( final int number, final long gasUsed, final int txCount) { Block mockBlock = mock(Block.class, RETURNS_DEEP_STUBS); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index 36e4ff8b8a5..4174fdf8313 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -109,6 +109,7 @@ public enum RpcMethod { ETH_GET_FILTER_CHANGES("eth_getFilterChanges"), ETH_GET_FILTER_LOGS("eth_getFilterLogs"), ETH_GET_LOGS("eth_getLogs"), + ETH_GET_MAX_PRIORITY_FEE_PER_GAS("eth_maxPriorityFeePerGas"), ETH_GET_MINER_DATA_BY_BLOCK_HASH("eth_getMinerDataByBlockHash"), ETH_GET_MINER_DATA_BY_BLOCK_NUMBER("eth_getMinerDataByBlockNumber"), ETH_GET_PROOF("eth_getProof"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthMaxPriorityFeePerGas.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthMaxPriorityFeePerGas.java new file mode 100644 index 00000000000..2828fee7aca --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthMaxPriorityFeePerGas.java @@ -0,0 +1,54 @@ +/* + * 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.ethereum.api.jsonrpc.internal.methods; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; + +import java.util.Optional; + +public class EthMaxPriorityFeePerGas implements JsonRpcMethod { + + private final BlockchainQueries blockchainQueries; + private final MiningCoordinator miningCoordinator; + + public EthMaxPriorityFeePerGas( + final BlockchainQueries blockchainQueries, final MiningCoordinator miningCoordinator) { + this.blockchainQueries = blockchainQueries; + this.miningCoordinator = miningCoordinator; + } + + @Override + public String getName() { + return RpcMethod.ETH_GET_MAX_PRIORITY_FEE_PER_GAS.getMethodName(); + } + + @Override + public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { + return new JsonRpcSuccessResponse( + requestContext.getRequest().getId(), Quantity.create(fetchAndLimitPriorityFeePerGas())); + } + + private Wei fetchAndLimitPriorityFeePerGas() { + final Optional gasPrice = blockchainQueries.gasPriorityFee(); + return gasPrice.orElseGet(miningCoordinator::getMinPriorityFeePerGas); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayload.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayload.java index db7ae1f511d..83da01fced1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayload.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayload.java @@ -14,9 +14,9 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.rpc.JsonRpcResponse; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; @@ -70,10 +70,9 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) { final PayloadIdentifier payloadId = request.getRequiredParameter(0, PayloadIdentifier.class); mergeMiningCoordinator.finalizeProposalById(payloadId); - final Optional blockWithReceipts = - mergeContext.get().retrieveBlockById(payloadId); - if (blockWithReceipts.isPresent()) { - final BlockWithReceipts proposal = blockWithReceipts.get(); + final Optional maybePayload = mergeContext.get().retrievePayloadById(payloadId); + if (maybePayload.isPresent()) { + final BlockWithReceipts proposal = maybePayload.get().blockWithReceipts(); LOG.atDebug() .setMessage("assembledBlock for payloadId {}: {}") .addArgument(() -> payloadId) @@ -85,37 +84,33 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) { if (!forkValidationResult.isValid()) { return new JsonRpcErrorResponse(request.getRequest().getId(), forkValidationResult); } - return createResponse(request, payloadId, proposal); + return createResponse(request, maybePayload.get()); } return new JsonRpcErrorResponse(request.getRequest().getId(), RpcErrorType.UNKNOWN_PAYLOAD); } - protected void logProposal( - final PayloadIdentifier payloadId, - final BlockWithReceipts proposal, - final Optional maybeReward) { - final BlockHeader proposalHeader = proposal.getHeader(); + protected void logProposal(final PayloadWrapper payload) { + final BlockHeader proposalHeader = payload.blockWithReceipts().getHeader(); final float gasUsedPerc = 100.0f * proposalHeader.getGasUsed() / proposalHeader.getGasLimit(); final String message = "Fetch block proposal by identifier: {}, hash: {}, " + "number: {}, coinbase: {}, transaction count: {}, gas used: {}%" - + maybeReward.map(unused -> ", reward: {}").orElse("{}"); + + " reward: {}"; LOG.atInfo() .setMessage(message) - .addArgument(payloadId::toHexString) + .addArgument(payload.payloadIdentifier()::toHexString) .addArgument(proposalHeader::getHash) .addArgument(proposalHeader::getNumber) .addArgument(proposalHeader::getCoinbase) - .addArgument(() -> proposal.getBlock().getBody().getTransactions().size()) + .addArgument( + () -> payload.blockWithReceipts().getBlock().getBody().getTransactions().size()) .addArgument(() -> String.format("%1.2f", gasUsedPerc)) - .addArgument(maybeReward.map(Wei::toHumanReadableString).orElse("")) + .addArgument(payload.blockValue()::toHumanReadableString) .log(); } protected abstract JsonRpcResponse createResponse( - final JsonRpcRequestContext request, - final PayloadIdentifier payloadId, - final BlockWithReceipts blockWithReceipts); + final JsonRpcRequestContext request, final PayloadWrapper payload); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV1.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV1.java index fec8473a36c..61b4174f198 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV1.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV1.java @@ -14,17 +14,14 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; -import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.rpc.JsonRpcResponse; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; - -import java.util.Optional; import io.vertx.core.Vertx; @@ -46,12 +43,10 @@ public String getName() { @Override protected JsonRpcResponse createResponse( - final JsonRpcRequestContext request, - final PayloadIdentifier payloadId, - final BlockWithReceipts blockWithReceipts) { + final JsonRpcRequestContext request, final PayloadWrapper payload) { final var result = - blockResultFactory.payloadTransactionCompleteV1(blockWithReceipts.getBlock()); - logProposal(payloadId, blockWithReceipts, Optional.empty()); + blockResultFactory.payloadTransactionCompleteV1(payload.blockWithReceipts().getBlock()); + logProposal(payload); return new JsonRpcSuccessResponse(request.getRequest().getId(), result); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java index ba9d4317ce4..7cba70ccee0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java @@ -14,9 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; -import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.rpc.JsonRpcResponse; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; @@ -24,7 +23,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; @@ -61,12 +59,9 @@ public String getName() { @Override protected JsonRpcResponse createResponse( - final JsonRpcRequestContext request, - final PayloadIdentifier payloadId, - final BlockWithReceipts blockWithReceipts) { - final var result = blockResultFactory.payloadTransactionCompleteV2(blockWithReceipts); - logProposal( - payloadId, blockWithReceipts, Optional.of(Wei.fromHexString(result.getBlockValue()))); + final JsonRpcRequestContext request, final PayloadWrapper payload) { + final var result = blockResultFactory.payloadTransactionCompleteV2(payload); + logProposal(payload); return new JsonRpcSuccessResponse(request.getRequest().getId(), result); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java index 4a148d6a752..11a660d4de4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java @@ -14,8 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; -import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.rpc.JsonRpcResponse; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; @@ -23,7 +23,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; @@ -60,13 +59,10 @@ public String getName() { @Override protected JsonRpcResponse createResponse( - final JsonRpcRequestContext request, - final PayloadIdentifier payloadId, - final BlockWithReceipts blockWithReceipts) { + final JsonRpcRequestContext request, final PayloadWrapper payload) { return new JsonRpcSuccessResponse( - request.getRequest().getId(), - blockResultFactory.payloadTransactionCompleteV3(blockWithReceipts)); + request.getRequest().getId(), blockResultFactory.payloadTransactionCompleteV3(payload)); } @Override diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java index 9262344b561..0447b7104ed 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java @@ -14,8 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; -import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.rpc.JsonRpcResponse; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; @@ -23,7 +23,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; @@ -60,13 +59,10 @@ public String getName() { @Override protected JsonRpcResponse createResponse( - final JsonRpcRequestContext request, - final PayloadIdentifier payloadId, - final BlockWithReceipts blockWithReceipts) { + final JsonRpcRequestContext request, final PayloadWrapper payload) { return new JsonRpcSuccessResponse( - request.getRequest().getId(), - blockResultFactory.payloadTransactionCompleteV4(blockWithReceipts)); + request.getRequest().getId(), blockResultFactory.payloadTransactionCompleteV4(payload)); } @Override diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java index aff5713afc6..2f89ef12ce1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/BlockResultFactory.java @@ -17,16 +17,14 @@ import static org.hyperledger.besu.ethereum.mainnet.requests.RequestUtil.getDepositRequests; import static org.hyperledger.besu.ethereum.mainnet.requests.RequestUtil.getWithdrawalRequests; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadBodiesResultV1.PayloadBody; import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata; import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.BlockValueCalculator; -import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.encoding.EncodingContext; import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; @@ -108,8 +106,8 @@ public EngineGetPayloadResultV1 payloadTransactionCompleteV1(final Block block) return new EngineGetPayloadResultV1(block.getHeader(), txs); } - public EngineGetPayloadResultV2 payloadTransactionCompleteV2( - final BlockWithReceipts blockWithReceipts) { + public EngineGetPayloadResultV2 payloadTransactionCompleteV2(final PayloadWrapper payload) { + final var blockWithReceipts = payload.blockWithReceipts(); final List txs = blockWithReceipts.getBlock().getBody().getTransactions().stream() .map( @@ -118,12 +116,11 @@ public EngineGetPayloadResultV2 payloadTransactionCompleteV2( .map(Bytes::toHexString) .collect(Collectors.toList()); - final Wei blockValue = new BlockValueCalculator().calculateBlockValue(blockWithReceipts); return new EngineGetPayloadResultV2( blockWithReceipts.getHeader(), txs, blockWithReceipts.getBlock().getBody().getWithdrawals(), - Quantity.create(blockValue)); + Quantity.create(payload.blockValue())); } public EngineGetPayloadBodiesResultV1 payloadBodiesCompleteV1( @@ -135,8 +132,8 @@ public EngineGetPayloadBodiesResultV1 payloadBodiesCompleteV1( return new EngineGetPayloadBodiesResultV1(payloadBodies); } - public EngineGetPayloadResultV3 payloadTransactionCompleteV3( - final BlockWithReceipts blockWithReceipts) { + public EngineGetPayloadResultV3 payloadTransactionCompleteV3(final PayloadWrapper payload) { + final var blockWithReceipts = payload.blockWithReceipts(); final List txs = blockWithReceipts.getBlock().getBody().getTransactions().stream() .map( @@ -145,20 +142,18 @@ public EngineGetPayloadResultV3 payloadTransactionCompleteV3( .map(Bytes::toHexString) .collect(Collectors.toList()); - final Wei blockValue = new BlockValueCalculator().calculateBlockValue(blockWithReceipts); - final BlobsBundleV1 blobsBundleV1 = new BlobsBundleV1(blockWithReceipts.getBlock().getBody().getTransactions()); return new EngineGetPayloadResultV3( blockWithReceipts.getHeader(), txs, blockWithReceipts.getBlock().getBody().getWithdrawals(), - Quantity.create(blockValue), + Quantity.create(payload.blockValue()), blobsBundleV1); } - public EngineGetPayloadResultV4 payloadTransactionCompleteV4( - final BlockWithReceipts blockWithReceipts) { + public EngineGetPayloadResultV4 payloadTransactionCompleteV4(final PayloadWrapper payload) { + final var blockWithReceipts = payload.blockWithReceipts(); final List txs = blockWithReceipts.getBlock().getBody().getTransactions().stream() .map( @@ -167,8 +162,6 @@ public EngineGetPayloadResultV4 payloadTransactionCompleteV4( .map(Bytes::toHexString) .collect(Collectors.toList()); - final Wei blockValue = new BlockValueCalculator().calculateBlockValue(blockWithReceipts); - final BlobsBundleV1 blobsBundleV1 = new BlobsBundleV1(blockWithReceipts.getBlock().getBody().getTransactions()); return new EngineGetPayloadResultV4( @@ -177,7 +170,7 @@ public EngineGetPayloadResultV4 payloadTransactionCompleteV4( blockWithReceipts.getBlock().getBody().getWithdrawals(), getDepositRequests(blockWithReceipts.getBlock().getBody().getRequests()), getWithdrawalRequests(blockWithReceipts.getBlock().getBody().getRequests()), - Quantity.create(blockValue), + Quantity.create(payload.blockValue()), blobsBundleV1); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java index 678df31824e..c7575a5435a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java @@ -52,6 +52,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthGetUncleCountByBlockNumber; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthGetWork; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthHashrate; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthMaxPriorityFeePerGas; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthMining; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthNewBlockFilter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthNewFilter; @@ -183,6 +184,7 @@ protected Map create() { new EthChainId(protocolSchedule.getChainId()), new EthGetMinerDataByBlockHash(blockchainQueries, protocolSchedule), new EthGetMinerDataByBlockNumber(blockchainQueries, protocolSchedule), - new EthBlobBaseFee(blockchainQueries.getBlockchain(), protocolSchedule)); + new EthBlobBaseFee(blockchainQueries.getBlockchain(), protocolSchedule), + new EthMaxPriorityFeePerGas(blockchainQueries, miningCoordinator)); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthMaxPriorityFeePerGasTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthMaxPriorityFeePerGasTest.java new file mode 100644 index 00000000000..02d526107a1 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthMaxPriorityFeePerGasTest.java @@ -0,0 +1,133 @@ +/* + * 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.ethereum.api.jsonrpc.internal.methods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.internal.verification.VerificationModeFactory; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class EthMaxPriorityFeePerGasTest { + private static final String JSON_RPC_VERSION = "2.0"; + private static final String ETH_METHOD = + RpcMethod.ETH_GET_MAX_PRIORITY_FEE_PER_GAS.getMethodName(); + private EthMaxPriorityFeePerGas method; + + @Mock private BlockchainQueries blockchainQueries; + @Mock private MiningCoordinator miningCoordinator; + + @BeforeEach + public void setUp() { + method = createEthMaxPriorityFeePerGasMethod(); + } + + @Test + public void shouldReturnCorrectMethodName() { + assertThat(method.getName()).isEqualTo(ETH_METHOD); + } + + @Test + public void whenNoTransactionsExistReturnMinPriorityFeePerGasPrice() { + final JsonRpcRequestContext request = requestWithParams(); + final String expectedWei = Wei.ONE.toShortHexString(); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); + when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(Wei.ONE); + + mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.empty()); + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + verify(miningCoordinator, VerificationModeFactory.times(1)).getMinPriorityFeePerGas(); + } + + @ParameterizedTest + @MethodSource("minPriorityFeePerGasValues") + public void whenNoTransactionsExistReturnMinPriorityFeePerGasPriceExist( + final Wei minPriorityFeePerGasValue) { + final JsonRpcRequestContext request = requestWithParams(); + final String expectedWei = minPriorityFeePerGasValue.toShortHexString(); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); + when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(minPriorityFeePerGasValue); + + mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.empty()); + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + verify(miningCoordinator, VerificationModeFactory.times(1)).getMinPriorityFeePerGas(); + } + + @Test + public void whenNoTransactionsExistReturnNullMinPriorityFeePerGasPriceExist() { + final JsonRpcRequestContext request = requestWithParams(); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(request.getRequest().getId(), null); + when(miningCoordinator.getMinPriorityFeePerGas()).thenReturn(null); + + mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.empty()); + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + verify(miningCoordinator, VerificationModeFactory.times(1)).getMinPriorityFeePerGas(); + } + + @Test + public void whenTransactionsExistReturnMaxPriorityFeePerGasPrice() { + final JsonRpcRequestContext request = requestWithParams(); + final String expectedWei = Wei.of(2000000000).toShortHexString(); + final JsonRpcResponse expectedResponse = + new JsonRpcSuccessResponse(request.getRequest().getId(), expectedWei); + mockBlockchainQueriesMaxPriorityFeePerGasPrice(Optional.of(Wei.of(2000000000))); + final JsonRpcResponse actualResponse = method.response(request); + assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); + verify(miningCoordinator, VerificationModeFactory.times(0)).getMinPriorityFeePerGas(); + } + + private static Stream minPriorityFeePerGasValues() { + return Stream.of(Arguments.of(Wei.ONE), Arguments.of(Wei.ZERO)); + } + + private void mockBlockchainQueriesMaxPriorityFeePerGasPrice(final Optional result) { + when(blockchainQueries.gasPriorityFee()).thenReturn(result); + } + + private JsonRpcRequestContext requestWithParams(final Object... params) { + return new JsonRpcRequestContext(new JsonRpcRequest(JSON_RPC_VERSION, ETH_METHOD, params)); + } + + private EthMaxPriorityFeePerGas createEthMaxPriorityFeePerGasMethod() { + return new EthMaxPriorityFeePerGas(blockchainQueries, miningCoordinator); + } +} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayloadTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayloadTest.java index 5089c5e689e..9043d8b09d8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayloadTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineGetPayloadTest.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.consensus.merge.MergeContext; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.crypto.KeyPair; @@ -97,6 +98,8 @@ public AbstractEngineGetPayloadTest() { new Block(mockHeader, new BlockBody(Collections.emptyList(), Collections.emptyList())); protected static final BlockWithReceipts mockBlockWithReceipts = new BlockWithReceipts(mockBlock, Collections.emptyList()); + protected static final PayloadWrapper mockPayload = + new PayloadWrapper(mockPid, mockBlockWithReceipts); private static final Block mockBlockWithWithdrawals = new Block( mockHeader, @@ -115,9 +118,13 @@ public AbstractEngineGetPayloadTest() { Optional.of(Collections.emptyList()))); protected static final BlockWithReceipts mockBlockWithReceiptsAndWithdrawals = new BlockWithReceipts(mockBlockWithWithdrawals, Collections.emptyList()); + protected static final PayloadWrapper mockPayloadWithWithdrawals = + new PayloadWrapper(mockPid, mockBlockWithReceiptsAndWithdrawals); protected static final BlockWithReceipts mockBlockWithReceiptsAndDepositRequests = new BlockWithReceipts(mockBlockWithDepositRequests, Collections.emptyList()); + protected static final PayloadWrapper mockPayloadWithDepositRequests = + new PayloadWrapper(mockPid, mockBlockWithReceiptsAndDepositRequests); @Mock protected ProtocolContext protocolContext; @@ -130,7 +137,7 @@ public AbstractEngineGetPayloadTest() { @Override public void before() { super.before(); - when(mergeContext.retrieveBlockById(mockPid)).thenReturn(Optional.of(mockBlockWithReceipts)); + when(mergeContext.retrievePayloadById(mockPid)).thenReturn(Optional.of(mockPayload)); when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext)); if (methodFactory.isPresent()) { this.method = diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java index 72f89c79c3a..5f42f02c82b 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java @@ -48,9 +48,7 @@ public EngineGetPayloadV2Test() { @Override public void before() { super.before(); - lenient() - .when(mergeContext.retrieveBlockById(mockPid)) - .thenReturn(Optional.of(mockBlockWithReceipts)); + lenient().when(mergeContext.retrievePayloadById(mockPid)).thenReturn(Optional.of(mockPayload)); when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext)); this.method = new EngineGetPayloadV2( @@ -72,8 +70,8 @@ public void shouldReturnExpectedMethodName() { @Test public void shouldReturnBlockForKnownPayloadId() { // should return withdrawals for a post-Shanghai block - when(mergeContext.retrieveBlockById(mockPid)) - .thenReturn(Optional.of(mockBlockWithReceiptsAndWithdrawals)); + when(mergeContext.retrievePayloadById(mockPid)) + .thenReturn(Optional.of(mockPayloadWithWithdrawals)); final var resp = resp(RpcMethod.ENGINE_GET_PAYLOAD_V2.getMethodName(), mockPid); assertThat(resp).isInstanceOf(JsonRpcSuccessResponse.class); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3Test.java index 00db0171b7d..ac589c11219 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3Test.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.BlobGas; @@ -67,9 +68,7 @@ public EngineGetPayloadV3Test() { @Override public void before() { super.before(); - lenient() - .when(mergeContext.retrieveBlockById(mockPid)) - .thenReturn(Optional.of(mockBlockWithReceipts)); + lenient().when(mergeContext.retrievePayloadById(mockPid)).thenReturn(Optional.of(mockPayload)); when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext)); this.method = new EngineGetPayloadV3( @@ -132,8 +131,10 @@ public void shouldReturnBlockForKnownPayloadId() { Optional.of(Collections.emptyList()), Optional.of(Collections.emptyList()))), List.of(blobReceipt)); + PayloadWrapper payloadPostCancun = new PayloadWrapper(postCancunPid, postCancunBlock); - when(mergeContext.retrieveBlockById(postCancunPid)).thenReturn(Optional.of(postCancunBlock)); + when(mergeContext.retrievePayloadById(postCancunPid)) + .thenReturn(Optional.of(payloadPostCancun)); final var resp = resp(RpcMethod.ENGINE_GET_PAYLOAD_V3.getMethodName(), postCancunPid); assertThat(resp).isInstanceOf(JsonRpcSuccessResponse.class); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4Test.java index e8e4e821a37..535b2c9983e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4Test.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.BlobGas; @@ -70,8 +71,8 @@ public EngineGetPayloadV4Test() { public void before() { super.before(); lenient() - .when(mergeContext.retrieveBlockById(mockPid)) - .thenReturn(Optional.of(mockBlockWithReceiptsAndDepositRequests)); + .when(mergeContext.retrievePayloadById(mockPid)) + .thenReturn(Optional.of(mockPayloadWithDepositRequests)); when(protocolContext.safeConsensusContext(Mockito.any())).thenReturn(Optional.of(mergeContext)); this.method = new EngineGetPayloadV4( @@ -134,8 +135,9 @@ public void shouldReturnBlockForKnownPayloadId() { Optional.of(Collections.emptyList()), Optional.of(Collections.emptyList()))), List.of(blobReceipt)); + PayloadWrapper payload = new PayloadWrapper(payloadIdentifier, block); - when(mergeContext.retrieveBlockById(payloadIdentifier)).thenReturn(Optional.of(block)); + when(mergeContext.retrievePayloadById(payloadIdentifier)).thenReturn(Optional.of(payload)); final var resp = resp(RpcMethod.ENGINE_GET_PAYLOAD_V4.getMethodName(), payloadIdentifier); assertThat(resp).isInstanceOf(JsonRpcSuccessResponse.class); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockValueCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockValueCalculator.java index c7552c227f8..a0db4717278 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockValueCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockValueCalculator.java @@ -20,7 +20,7 @@ public class BlockValueCalculator { - public Wei calculateBlockValue(final BlockWithReceipts blockWithReceipts) { + public static Wei calculateBlockValue(final BlockWithReceipts blockWithReceipts) { final Block block = blockWithReceipts.getBlock(); final List txs = block.getBody().getTransactions(); final List receipts = blockWithReceipts.getReceipts(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 0027c51b79d..51d300cbc46 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -59,6 +59,8 @@ public class MainnetTransactionProcessor { private static final Logger LOG = LoggerFactory.getLogger(MainnetTransactionProcessor.class); + private static final Set
EMPTY_ADDRESS_SET = Set.of(); + protected final GasCalculator gasCalculator; protected final TransactionValidatorFactory transactionValidatorFactory; @@ -451,15 +453,6 @@ public TransactionProcessingResult processTransaction( .log(); final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas(); - operationTracer.traceEndTransaction( - worldUpdater, - transaction, - initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS, - initialFrame.getOutputData(), - initialFrame.getLogs(), - gasUsedByTransaction, - 0L); - // update the coinbase final var coinbase = worldState.getOrCreate(miningBeneficiary); final long usedGas = transaction.getGasLimit() - refundedGas; @@ -485,6 +478,16 @@ public TransactionProcessingResult processTransaction( coinbase.incrementBalance(coinbaseWeiDelta); + operationTracer.traceEndTransaction( + worldUpdater, + transaction, + initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS, + initialFrame.getOutputData(), + initialFrame.getLogs(), + gasUsedByTransaction, + initialFrame.getSelfDestructs(), + 0L); + initialFrame.getSelfDestructs().forEach(worldState::deleteAccount); if (clearEmptyAccounts) { @@ -516,13 +519,27 @@ public TransactionProcessingResult processTransaction( } } catch (final MerkleTrieException re) { operationTracer.traceEndTransaction( - worldState.updater(), transaction, false, Bytes.EMPTY, List.of(), 0, 0L); + worldState.updater(), + transaction, + false, + Bytes.EMPTY, + List.of(), + 0, + EMPTY_ADDRESS_SET, + 0L); // need to throw to trigger the heal throw re; } catch (final RuntimeException re) { operationTracer.traceEndTransaction( - worldState.updater(), transaction, false, Bytes.EMPTY, List.of(), 0, 0L); + worldState.updater(), + transaction, + false, + Bytes.EMPTY, + List.of(), + 0, + EMPTY_ADDRESS_SET, + 0L); LOG.error("Critical Exception Processing Transaction", re); return TransactionProcessingResult.invalid( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DataStorageConfiguration.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DataStorageConfiguration.java index a14f86bbb5f..646e67d2da5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DataStorageConfiguration.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DataStorageConfiguration.java @@ -23,6 +23,9 @@ public interface DataStorageConfiguration { long DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD = 512; + boolean DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED = true; + long MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; + int DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = 30_000; boolean DEFAULT_RECEIPT_COMPACTION_ENABLED = false; DataStorageConfiguration DEFAULT_CONFIG = @@ -56,6 +59,16 @@ public interface DataStorageConfiguration { Long getBonsaiMaxLayersToLoad(); + @Value.Default + default boolean getBonsaiLimitTrieLogsEnabled() { + return DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; + } + + @Value.Default + default int getBonsaiTrieLogPruningWindowSize() { + return DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; + } + @Value.Default default boolean getReceiptCompactionEnabled() { return DEFAULT_RECEIPT_COMPACTION_ENABLED; @@ -69,9 +82,6 @@ default Unstable getUnstable() { @Value.Immutable interface Unstable { - boolean DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED = false; - long MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; - int DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = 30_000; boolean DEFAULT_BONSAI_FULL_FLAT_DB_ENABLED = true; boolean DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED = true; @@ -81,16 +91,6 @@ interface Unstable { DataStorageConfiguration.Unstable DEFAULT_PARTIAL = ImmutableDataStorageConfiguration.Unstable.builder().bonsaiFullFlatDbEnabled(false).build(); - @Value.Default - default boolean getBonsaiLimitTrieLogsEnabled() { - return DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; - } - - @Value.Default - default int getBonsaiTrieLogPruningWindowSize() { - return DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; - } - @Value.Default default boolean getBonsaiFullFlatDbEnabled() { return DEFAULT_BONSAI_FULL_FLAT_DB_ENABLED; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/BlockValueCalculatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/BlockValueCalculatorTest.java index 0db0ec4145d..32dc063f313 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/BlockValueCalculatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/BlockValueCalculatorTest.java @@ -41,8 +41,8 @@ public void shouldCalculateZeroBlockValueForEmptyTransactions() { final Block block = new Block(blockHeader, new BlockBody(Collections.emptyList(), Collections.emptyList())); Wei blockValue = - new BlockValueCalculator() - .calculateBlockValue(new BlockWithReceipts(block, Collections.emptyList())); + BlockValueCalculator.calculateBlockValue( + new BlockWithReceipts(block, Collections.emptyList())); assertThat(blockValue).isEqualTo(Wei.ZERO); } @@ -85,9 +85,8 @@ public void shouldCalculateCorrectBlockValue() { final Block block = new Block(blockHeader, new BlockBody(List.of(tx1, tx2, tx3), Collections.emptyList())); Wei blockValue = - new BlockValueCalculator() - .calculateBlockValue( - new BlockWithReceipts(block, List.of(receipt1, receipt2, receipt3))); + BlockValueCalculator.calculateBlockValue( + new BlockWithReceipts(block, List.of(receipt1, receipt2, receipt3))); // Block value = 71 * 1 + (143-71) * 2 + (214-143) * 5 = 1427 assertThat(blockValue).isEqualTo(Wei.of(570L)); } @@ -114,8 +113,7 @@ public void shouldCalculateCorrectBlockValueExceedingLong() { final Block block = new Block(blockHeader, new BlockBody(List.of(tx1), Collections.emptyList())); Wei blockValue = - new BlockValueCalculator() - .calculateBlockValue(new BlockWithReceipts(block, List.of(receipt1))); + BlockValueCalculator.calculateBlockValue(new BlockWithReceipts(block, List.of(receipt1))); // Block value =~ max_long * 2 assertThat(blockValue).isGreaterThan(Wei.of(Long.MAX_VALUE)); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java index 77466587314..d541cbd99aa 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; @@ -216,6 +217,7 @@ public void traceEndTransaction( final Bytes output, final List logs, final long gasUsed, + final Set
selfDestructs, final long timeNs) { this.traceEndTxCalled = true; } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index 6e48aaf82ca..ccabce833a6 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -69,6 +69,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.concurrent.TimeUnit; @@ -86,6 +87,8 @@ public class T8nExecutor { + private static final Set
EMPTY_ADDRESS_SET = Set.of(); + public record RejectedTransaction(int index, String error) {} protected static List extractTransactions( @@ -349,6 +352,7 @@ static T8nResult runTest( result.getOutput(), result.getLogs(), gasUsed - intrinsicGas, + EMPTY_ADDRESS_SET, timer.elapsed(TimeUnit.NANOSECONDS)); Bytes gasUsedInTransaction = Bytes.ofUnsignedLong(transactionGasUsed); receipts.add(receipt); diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemon.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemon.java index d17c846be79..ef794ae2047 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemon.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemon.java @@ -88,7 +88,6 @@ public void start() { public void stop() { LOG.info("Stopping DNSDaemon for {}", enrLink); periodicTaskId.ifPresent(vertx::cancelTimer); - dnsResolver.close(); } /** diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSResolver.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSResolver.java index d42304ed043..0be4ca619d1 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSResolver.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSResolver.java @@ -24,12 +24,9 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicReference; import com.google.common.base.Splitter; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.dns.DnsClient; import io.vertx.core.dns.DnsClientOptions; @@ -42,9 +39,8 @@ // Adapted from https://github.com/tmio/tuweni and licensed under Apache 2.0 /** Resolves a set of ENR nodes from a host name. */ -public class DNSResolver implements AutoCloseable { +public class DNSResolver { private static final Logger LOG = LoggerFactory.getLogger(DNSResolver.class); - private final ExecutorService rawTxtRecordsExecutor = Executors.newSingleThreadExecutor(); private final String enrLink; private long seq; private final DnsClient dnsClient; @@ -118,7 +114,7 @@ public long sequence() { private void visitTree(final ENRTreeLink link, final DNSVisitor visitor) { Optional optionalEntry = resolveRecord(link.domainName()); if (optionalEntry.isEmpty()) { - LOG.debug("No DNS record found for {}", link.domainName()); + LOG.trace("No DNS record found for {}", link.domainName()); return; } @@ -146,32 +142,30 @@ private boolean internalVisit( final String entryName, final String domainName, final DNSVisitor visitor) { final Optional optionalDNSEntry = resolveRecord(entryName + "." + domainName); if (optionalDNSEntry.isEmpty()) { - LOG.debug("No DNS record found for {}", entryName + "." + domainName); return true; } final DNSEntry entry = optionalDNSEntry.get(); - if (entry instanceof ENRNode node) { - // TODO: this always return true because the visitor is reference to list.add - return visitor.visit(node.nodeRecord()); - } else if (entry instanceof DNSEntry.ENRTree tree) { - for (String e : tree.entries()) { - // TODO: When would this ever return false? - boolean keepGoing = internalVisit(e, domainName, visitor); - if (!keepGoing) { - return false; + switch (entry) { + case ENRNode node -> { + return visitor.visit(node.nodeRecord()); + } + case DNSEntry.ENRTree tree -> { + for (String e : tree.entries()) { + boolean keepGoing = internalVisit(e, domainName, visitor); + if (!keepGoing) { + return false; + } } } - } else if (entry instanceof ENRTreeLink link) { - visitTree(link, visitor); - } else { - LOG.debug("Unsupported type of node {}", entry); + case ENRTreeLink link -> visitTree(link, visitor); + default -> LOG.debug("Unsupported type of node {}", entry); } return true; } /** - * Resolves one DNS record associated with the given domain name. + * Maps TXT DNS record to DNSEntry. * * @param domainName the domain name to query * @return the DNS entry read from the domain. Empty if no record is found. @@ -187,51 +181,21 @@ Optional resolveRecord(final String domainName) { * @return the first TXT entry of the DNS record. Empty if no record is found. */ Optional resolveRawRecord(final String domainName) { - // vertx-dns is async, kotlin coroutines allows us to await, similarly Java 21 new thread - // model would also allow us to await. For now, we will use CountDownLatch to block the - // current thread until the DNS resolution is complete. - LOG.debug("Resolving TXT records on domain: {}", domainName); - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference> record = new AtomicReference<>(Optional.empty()); - rawTxtRecordsExecutor.submit( - () -> { - dnsClient - .resolveTXT(domainName) - .onComplete( - ar -> { - if (ar.succeeded()) { - LOG.trace( - "TXT record resolved on domain {}. Result: {}", domainName, ar.result()); - record.set(ar.result().stream().findFirst()); - } else { - LOG.trace( - "TXT record not resolved on domain {}, because: {}", - domainName, - ar.cause().getMessage()); - } - latch.countDown(); - }); - }); - + LOG.trace("Resolving TXT records on domain: {}", domainName); try { - // causes the worker thread to wait. Once we move to Java 21, this can be simplified. - latch.await(); - } catch (InterruptedException e) { - LOG.debug("Interrupted while waiting for DNS resolution"); + // Future.await parks current virtual thread and waits for the result. Any failure is + // thrown as a Throwable. + return Future.await(dnsClient.resolveTXT(domainName)).stream().findFirst(); + } catch (final Throwable e) { + LOG.trace("Error while resolving TXT records on domain: {}", domainName, e); + return Optional.empty(); } - - return record.get(); } private boolean checkSignature( final ENRTreeRoot root, final SECP256K1.PublicKey pubKey, final SECP256K1.Signature sig) { - Bytes32 hash = + final Bytes32 hash = Hash.keccak256(Bytes.wrap(root.signedContent().getBytes(StandardCharsets.UTF_8))); return SECP256K1.verifyHashed(hash, sig, pubKey); } - - @Override - public void close() { - rawTxtRecordsExecutor.shutdown(); - } } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java index 573effa173a..55a536976fe 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/network/DefaultP2PNetwork.java @@ -82,7 +82,6 @@ import io.vertx.core.Future; import io.vertx.core.ThreadingModel; import io.vertx.core.Vertx; -import org.apache.commons.lang3.tuple.Pair; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.devp2p.EthereumNodeRecord; import org.slf4j.Logger; @@ -152,7 +151,7 @@ public class DefaultP2PNetwork implements P2PNetwork { private final CountDownLatch shutdownLatch = new CountDownLatch(2); private final Duration shutdownTimeout = Duration.ofSeconds(15); private final Vertx vertx; - private final AtomicReference>> dnsDaemonRef = + private final AtomicReference> dnsDaemonRef = new AtomicReference<>(Optional.empty()); /** @@ -242,17 +241,16 @@ public void start() { 600000L, config.getDnsDiscoveryServerOverride().orElse(null)); - // TODO: Java 21, we can move to Virtual Thread model + // Use Java 21 virtual thread to deploy verticle final DeploymentOptions options = new DeploymentOptions() - .setThreadingModel(ThreadingModel.WORKER) + .setThreadingModel(ThreadingModel.VIRTUAL_THREAD) .setInstances(1) .setWorkerPoolSize(1); final Future deployId = vertx.deployVerticle(dnsDaemon, options); - final String dnsDaemonDeployId = - deployId.toCompletionStage().toCompletableFuture().join(); - dnsDaemonRef.set(Optional.of(Pair.of(dnsDaemonDeployId, dnsDaemon))); + deployId.toCompletionStage().toCompletableFuture().join(); + dnsDaemonRef.set(Optional.of(dnsDaemon)); }); final int listeningPort = rlpxAgent.start().join(); @@ -301,7 +299,7 @@ public void stop() { // since dnsDaemon is a vertx verticle, vertx.close will undeploy it. // However, we can safely call stop as well. - dnsDaemonRef.get().map(Pair::getRight).ifPresent(DNSDaemon::stop); + dnsDaemonRef.get().ifPresent(DNSDaemon::stop); peerConnectionScheduler.shutdownNow(); peerDiscoveryAgent.stop().whenComplete((res, err) -> shutdownLatch.countDown()); @@ -358,7 +356,7 @@ public boolean removeMaintainedConnectionPeer(final Peer peer) { @VisibleForTesting Optional getDnsDaemon() { - return dnsDaemonRef.get().map(Pair::getRight); + return dnsDaemonRef.get(); } @VisibleForTesting diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemonTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemonTest.java index cb465093d07..08539d47bf8 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemonTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/dns/DNSDaemonTest.java @@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.io.IOException; import java.security.Security; import java.util.concurrent.atomic.AtomicInteger; @@ -30,6 +29,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -38,12 +38,11 @@ class DNSDaemonTest { private static final String holeskyEnr = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.holesky.ethdisco.net"; - // private static MockDNSServer mockDNSServer; private final MockDnsServerVerticle mockDnsServerVerticle = new MockDnsServerVerticle(); private DNSDaemon dnsDaemon; @BeforeAll - static void setup() throws IOException { + static void setup() { Security.addProvider(new BouncyCastleProvider()); } @@ -68,11 +67,14 @@ void testDNSDaemon(final Vertx vertx, final VertxTestContext testContext) "localhost:" + mockDnsServerVerticle.port()); final DeploymentOptions options = - new DeploymentOptions().setThreadingModel(ThreadingModel.WORKER).setWorkerPoolSize(1); + new DeploymentOptions() + .setThreadingModel(ThreadingModel.VIRTUAL_THREAD) + .setWorkerPoolSize(1); vertx.deployVerticle(dnsDaemon, options); } @Test + @Disabled("this test is flaky") @DisplayName("Test DNS Daemon with periodic lookup to a mock DNS server") void testDNSDaemonPeriodic(final Vertx vertx, final VertxTestContext testContext) throws InterruptedException { @@ -109,7 +111,9 @@ void testDNSDaemonPeriodic(final Vertx vertx, final VertxTestContext testContext "localhost:" + mockDnsServerVerticle.port()); final DeploymentOptions options = - new DeploymentOptions().setThreadingModel(ThreadingModel.WORKER).setWorkerPoolSize(1); + new DeploymentOptions() + .setThreadingModel(ThreadingModel.VIRTUAL_THREAD) + .setWorkerPoolSize(1); vertx.deployVerticle(dnsDaemon, options); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java index 5bd6de591e5..34e28bbb32f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.evm.tracing; +import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -23,6 +24,7 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import org.apache.tuweni.bytes.Bytes; @@ -92,6 +94,7 @@ default void traceStartTransaction(final WorldView worldView, final Transaction * @param output the bytes output from the transaction * @param logs the logs emitted by this transaction * @param gasUsed the gas used by the entire transaction + * @param selfDestructs the set of addresses that self-destructed during the transaction * @param timeNs the time in nanoseconds it took to execute the transaction */ default void traceEndTransaction( @@ -101,6 +104,7 @@ default void traceEndTransaction( final Bytes output, final List logs, final long gasUsed, + final Set
selfDestructs, final long timeNs) {} /** diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a4f0..a4413138c96 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle b/settings.gradle index 81f4cb8736b..09a8d20d4c6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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'