Merge branch 'main' into zkbesu

This commit is contained in:
Daniel Lehrner
2024-06-11 14:49:46 +02:00
56 changed files with 1142 additions and 405 deletions

View File

@@ -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

View File

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

View File

@@ -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/**'

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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'
}

View File

@@ -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;
}
}

View File

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

View File

@@ -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();
}
}

View File

@@ -1578,7 +1578,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private void validateDataStorageOptions() {
dataStorageOptions.validate(commandLine);
dataStorageOptions.validate(commandLine, syncMode);
}
private void validateRequiredOptions() {
@@ -2808,11 +2808,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
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());

View File

@@ -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<DataStorageConfiguration>
paramLabel = "<LONG>",
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<DataStorageConfiguration>
/** 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 class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
* 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 class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
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 class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
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 class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
.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())

View File

@@ -17,7 +17,7 @@ package org.hyperledger.besu.cli.subcommands.storage;
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 @@ public class TrieLogHelper {
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()));
}

View File

@@ -733,7 +733,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
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 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
blockchain,
scheduler::executeServiceTask,
dataStorageConfiguration.getBonsaiMaxLayersToLoad(),
dataStorageConfiguration.getUnstable().getBonsaiTrieLogPruningWindowSize(),
dataStorageConfiguration.getBonsaiTrieLogPruningWindowSize(),
isProofOfStake);
trieLogPruner.initialize();

View File

@@ -1087,8 +1087,8 @@ public class BesuCommandTest extends CommandTestAbstract {
}
@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 class BesuCommandTest extends CommandTestAbstract {
}
@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 class BesuCommandTest extends CommandTestAbstract {
}
@Test
public void parsesInvalidBonsaiTrieLimitBackLayersOption() {
public void parsesInvalidBonsaiHistoricalBlockLimitOption() {
parseCommand("--data-storage-format", "BONSAI", "--bonsai-maximum-back-layers-to-load", "ten");

View File

@@ -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 class DataStorageOptionsTest
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 class DataStorageOptionsTest
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 class DataStorageOptionsTest
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 @@ public class DataStorageOptionsTest
return ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(DataStorageFormat.BONSAI)
.bonsaiMaxLayersToLoad(513L)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(true)
.bonsaiTrieLogPruningWindowSize(514)
.build())
.bonsaiLimitTrieLogsEnabled(true)
.bonsaiTrieLogPruningWindowSize(514)
.build();
}

View File

@@ -17,7 +17,7 @@ package org.hyperledger.besu.cli.subcommands.storage;
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 @@ class TrieLogHelperTest {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(BONSAI)
.bonsaiMaxLayersToLoad(3L)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(true)
.build())
.bonsaiLimitTrieLogsEnabled(true)
.build();
mockBlockchainBase();
@@ -176,10 +173,7 @@ class TrieLogHelperTest {
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 @@ class TrieLogHelperTest {
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 @@ class TrieLogHelperTest {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(BONSAI)
.bonsaiMaxLayersToLoad(2L)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(true)
.build())
.bonsaiLimitTrieLogsEnabled(true)
.build();
mockBlockchainBase();
@@ -246,10 +234,7 @@ class TrieLogHelperTest {
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 @@ class TrieLogHelperTest {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(BONSAI)
.bonsaiMaxLayersToLoad(3L)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(true)
.build())
.bonsaiLimitTrieLogsEnabled(true)
.build();
mockBlockchainBase();
@@ -303,10 +285,7 @@ class TrieLogHelperTest {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(BONSAI)
.bonsaiMaxLayersToLoad(511L)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(true)
.build())
.bonsaiLimitTrieLogsEnabled(true)
.build();
TrieLogHelper helper = new TrieLogHelper();
@@ -324,11 +303,8 @@ class TrieLogHelperTest {
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 @@ class TrieLogHelperTest {
() ->
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 @@ class TrieLogHelperTest {
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 @@ class TrieLogHelperTest {
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 @@ class TrieLogHelperTest {
ImmutableDataStorageConfiguration.builder()
.dataStorageFormat(BONSAI)
.bonsaiMaxLayersToLoad(3L)
.unstable(
ImmutableDataStorageConfiguration.Unstable.builder()
.bonsaiLimitTrieLogsEnabled(true)
.build())
.bonsaiLimitTrieLogsEnabled(true)
.build();
mockBlockchainBase();

View File

@@ -125,7 +125,7 @@ class TraceServiceImplTest {
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 @@ class TraceServiceImplTest {
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 @@ class TraceServiceImplTest {
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 @@ class TraceServiceImplTest {
public Bytes txEndOutput;
public List<Log> txEndLogs;
public long txEndGasUsed;
public Set<Address> txEndSelfDestructs;
public Long txEndTimeNs;
private final Set<Transaction> traceStartTxCalled = new HashSet<>();
@@ -287,6 +296,7 @@ class TraceServiceImplTest {
final Bytes output,
final List<Log> logs,
final long gasUsed,
final Set<Address> selfDestructs,
final long timeNs) {
if (!traceEndTxCalled.add(transaction)) {
fail("traceEndTransaction already called for tx " + transaction);
@@ -297,6 +307,7 @@ class TraceServiceImplTest {
txEndOutput = output;
txEndLogs = logs;
txEndGasUsed = gasUsed;
txEndSelfDestructs = selfDestructs;
txEndTimeNs = timeNs;
}

View File

@@ -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

View File

@@ -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.")

View File

@@ -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
min-gas-price=0
bonsai-limit-trie-logs-enabled=false

View File

@@ -1,4 +1,3 @@
sync-mode="CHECKPOINT"
data-storage-format="BONSAI"
bonsai-historical-block-limit=128
max-peers=25

View File

@@ -18,7 +18,6 @@ import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier;
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 @@ public interface MergeContext extends ConsensusContext {
* @param payloadId the payload identifier
* @return the optional block with receipts
*/
Optional<BlockWithReceipts> retrieveBlockById(final PayloadIdentifier payloadId);
Optional<PayloadWrapper> retrievePayloadById(final PayloadIdentifier payloadId);
/**
* Is configured for a post-merge from genesis.

View File

@@ -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;
}
}

View File

@@ -16,12 +16,9 @@ package org.hyperledger.besu.consensus.merge;
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 org.hyperledger.besu.util.Subscribers;
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<PostMergeContext> singleton = new AtomicReference<>();
private static final Comparator<BlockWithReceipts> compareByGasUsedDesc =
Comparator.comparingLong(
(BlockWithReceipts blockWithReceipts) ->
blockWithReceipts.getBlock().getHeader().getGasUsed())
.reversed();
private final AtomicReference<SyncState> syncState;
private final AtomicReference<Difficulty> terminalTotalDifficulty;
// initial postMerge state is indeterminate until it is set:
@@ -70,7 +59,6 @@ public class PostMergeContext implements MergeContext {
private final AtomicReference<BlockHeader> lastSafeBlock = new AtomicReference<>();
private final AtomicReference<Optional<BlockHeader>> 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 class PostMergeContext implements MergeContext {
}
@Override
public void putPayloadById(final PayloadWrapper payloadWrapper) {
synchronized (blocksInProgress) {
final Optional<BlockWithReceipts> maybeCurrBestBlock =
retrieveBlockById(payloadWrapper.payloadIdentifier());
public void putPayloadById(final PayloadWrapper newPayload) {
final var newBlockWithReceipts = newPayload.blockWithReceipts();
final var newBlockValue = newPayload.blockValue();
maybeCurrBestBlock.ifPresentOrElse(
currBestBlock -> {
if (compareByGasUsedDesc.compare(payloadWrapper.blockWithReceipts(), currBestBlock)
< 0) {
synchronized (blocksInProgress) {
final Optional<PayloadWrapper> maybeCurrBestPayload =
retrievePayloadById(newPayload.payloadIdentifier());
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<BlockWithReceipts> retrieveBlockById(final PayloadIdentifier payloadId) {
public Optional<PayloadWrapper> 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<PayloadWrapper> retrievePayloadsById(final PayloadIdentifier payloadId) {
private Stream<PayloadWrapper> streamPayloadsById(final PayloadIdentifier payloadId) {
return blocksInProgress.stream().filter(z -> z.payloadIdentifier().equals(payloadId));
}

View File

@@ -18,7 +18,6 @@ import org.hyperledger.besu.consensus.merge.blockcreation.PayloadIdentifier;
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 class TransitionContext implements MergeContext {
}
@Override
public Optional<BlockWithReceipts> retrieveBlockById(final PayloadIdentifier payloadId) {
return postMergeContext.retrieveBlockById(payloadId);
public Optional<PayloadWrapper> retrievePayloadById(final PayloadIdentifier payloadId) {
return postMergeContext.retrievePayloadById(payloadId);
}
@Override

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
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 class PostMergeContextTest {
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 class PostMergeContextTest {
BlockWithReceipts betterBlockWithReceipts = createBlockWithReceipts(2, 11, 1);
PayloadIdentifier payloadId = new PayloadIdentifier(1L);
postMergeContext.putPayloadById(new PayloadWrapper(payloadId, zeroTxBlockWithReceipts));
postMergeContext.putPayloadById(new PayloadWrapper(payloadId, 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.retrieveBlockById(payloadId)).contains(betterBlockWithReceipts);
assertThat(postMergeContext.retrievePayloadById(payloadId))
.map(PayloadWrapper::blockWithReceipts)
.contains(betterBlockWithReceipts);
}
@Test
@@ -162,25 +172,33 @@ public class PostMergeContextTest {
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));
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.retrieveBlockById(payloadId)).contains(betterBlockWithReceipts);
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 class PostMergeContextTest {
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);

View File

@@ -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"),

View File

@@ -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<Wei> gasPrice = blockchainQueries.gasPriorityFee();
return gasPrice.orElseGet(miningCoordinator::getMinPriorityFeePerGas);
}
}

View File

@@ -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 abstract class AbstractEngineGetPayload extends ExecutionEngineJsonRpcMet
final PayloadIdentifier payloadId = request.getRequiredParameter(0, PayloadIdentifier.class);
mergeMiningCoordinator.finalizeProposalById(payloadId);
final Optional<BlockWithReceipts> blockWithReceipts =
mergeContext.get().retrieveBlockById(payloadId);
if (blockWithReceipts.isPresent()) {
final BlockWithReceipts proposal = blockWithReceipts.get();
final Optional<PayloadWrapper> 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 abstract class AbstractEngineGetPayload extends ExecutionEngineJsonRpcMet
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<Wei> 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);
}

View File

@@ -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 class EngineGetPayloadV1 extends AbstractEngineGetPayload {
@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);
}
}

View File

@@ -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.JsonRpcRequestContext;
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 class EngineGetPayloadV2 extends AbstractEngineGetPayload {
@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);
}

View File

@@ -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.JsonRpcRequestContext;
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 class EngineGetPayloadV3 extends AbstractEngineGetPayload {
@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

View File

@@ -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.JsonRpcRequestContext;
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 class EngineGetPayloadV4 extends AbstractEngineGetPayload {
@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

View File

@@ -17,16 +17,14 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
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 class BlockResultFactory {
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<String> txs =
blockWithReceipts.getBlock().getBody().getTransactions().stream()
.map(
@@ -118,12 +116,11 @@ public class BlockResultFactory {
.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 class BlockResultFactory {
return new EngineGetPayloadBodiesResultV1(payloadBodies);
}
public EngineGetPayloadResultV3 payloadTransactionCompleteV3(
final BlockWithReceipts blockWithReceipts) {
public EngineGetPayloadResultV3 payloadTransactionCompleteV3(final PayloadWrapper payload) {
final var blockWithReceipts = payload.blockWithReceipts();
final List<String> txs =
blockWithReceipts.getBlock().getBody().getTransactions().stream()
.map(
@@ -145,20 +142,18 @@ public class BlockResultFactory {
.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<String> txs =
blockWithReceipts.getBlock().getBody().getTransactions().stream()
.map(
@@ -167,8 +162,6 @@ public class BlockResultFactory {
.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 class BlockResultFactory {
blockWithReceipts.getBlock().getBody().getWithdrawals(),
getDepositRequests(blockWithReceipts.getBlock().getBody().getRequests()),
getWithdrawalRequests(blockWithReceipts.getBlock().getBody().getRequests()),
Quantity.create(blockValue),
Quantity.create(payload.blockValue()),
blobsBundleV1);
}

View File

@@ -52,6 +52,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.EthGetUncleCou
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 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods {
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));
}
}

View File

@@ -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<Arguments> minPriorityFeePerGasValues() {
return Stream.of(Arguments.of(Wei.ONE), Arguments.of(Wei.ZERO));
}
private void mockBlockchainQueriesMaxPriorityFeePerGasPrice(final Optional<Wei> 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);
}
}

View File

@@ -20,6 +20,7 @@ import static org.mockito.Mockito.verify;
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 abstract class AbstractEngineGetPayloadTest extends AbstractScheduledApiT
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 abstract class AbstractEngineGetPayloadTest extends AbstractScheduledApiT
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 abstract class AbstractEngineGetPayloadTest extends AbstractScheduledApiT
@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 =

View File

@@ -48,9 +48,7 @@ public class EngineGetPayloadV2Test extends AbstractEngineGetPayloadTest {
@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 class EngineGetPayloadV2Test extends AbstractEngineGetPayloadTest {
@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);

View File

@@ -21,6 +21,7 @@ import static org.mockito.Mockito.times;
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 class EngineGetPayloadV3Test extends AbstractEngineGetPayloadTest {
@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 class EngineGetPayloadV3Test extends AbstractEngineGetPayloadTest {
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);

View File

@@ -21,6 +21,7 @@ import static org.mockito.Mockito.times;
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 class EngineGetPayloadV4Test extends AbstractEngineGetPayloadTest {
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 class EngineGetPayloadV4Test extends AbstractEngineGetPayloadTest {
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);

View File

@@ -20,7 +20,7 @@ import java.util.List;
public class BlockValueCalculator {
public Wei calculateBlockValue(final BlockWithReceipts blockWithReceipts) {
public static Wei calculateBlockValue(final BlockWithReceipts blockWithReceipts) {
final Block block = blockWithReceipts.getBlock();
final List<Transaction> txs = block.getBody().getTransactions();
final List<TransactionReceipt> receipts = blockWithReceipts.getReceipts();

View File

@@ -59,6 +59,8 @@ public class MainnetTransactionProcessor {
private static final Logger LOG = LoggerFactory.getLogger(MainnetTransactionProcessor.class);
private static final Set<Address> EMPTY_ADDRESS_SET = Set.of();
protected final GasCalculator gasCalculator;
protected final TransactionValidatorFactory transactionValidatorFactory;
@@ -451,15 +453,6 @@ public class MainnetTransactionProcessor {
.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 class MainnetTransactionProcessor {
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 class MainnetTransactionProcessor {
}
} 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(

View File

@@ -23,6 +23,9 @@ import org.immutables.value.Value;
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 @@ public interface DataStorageConfiguration {
@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 @@ public interface DataStorageConfiguration {
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;

View File

@@ -41,8 +41,8 @@ public class BlockValueCalculatorTest {
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 class BlockValueCalculatorTest {
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 class BlockValueCalculatorTest {
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));
}

View File

@@ -41,6 +41,7 @@ import org.hyperledger.besu.evm.worldstate.WorldView;
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 @@ class MainnetTransactionProcessorTest {
final Bytes output,
final List<Log> logs,
final long gasUsed,
final Set<Address> selfDestructs,
final long timeNs) {
this.traceEndTxCalled = true;
}

View File

@@ -69,6 +69,7 @@ import java.util.List;
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 @@ import org.apache.tuweni.units.bigints.UInt256;
public class T8nExecutor {
private static final Set<Address> EMPTY_ADDRESS_SET = Set.of();
public record RejectedTransaction(int index, String error) {}
protected static List<Transaction> extractTransactions(
@@ -349,6 +352,7 @@ public class T8nExecutor {
result.getOutput(),
result.getLogs(),
gasUsed - intrinsicGas,
EMPTY_ADDRESS_SET,
timer.elapsed(TimeUnit.NANOSECONDS));
Bytes gasUsedInTransaction = Bytes.ofUnsignedLong(transactionGasUsed);
receipts.add(receipt);

View File

@@ -88,7 +88,6 @@ public class DNSDaemon extends AbstractVerticle {
public void stop() {
LOG.info("Stopping DNSDaemon for {}", enrLink);
periodicTaskId.ifPresent(vertx::cancelTimer);
dnsResolver.close();
}
/**

View File

@@ -24,12 +24,9 @@ import java.util.ArrayList;
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 @@ import org.slf4j.LoggerFactory;
// 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 class DNSResolver implements AutoCloseable {
private void visitTree(final ENRTreeLink link, final DNSVisitor visitor) {
Optional<DNSEntry> 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 @@ public class DNSResolver implements AutoCloseable {
final String entryName, final String domainName, final DNSVisitor visitor) {
final Optional<DNSEntry> 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 @@ public class DNSResolver implements AutoCloseable {
* @return the first TXT entry of the DNS record. Empty if no record is found.
*/
Optional<String> 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<Optional<String>> 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();
}
}

View File

@@ -82,7 +82,6 @@ import io.vertx.core.DeploymentOptions;
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<Optional<Pair<String, DNSDaemon>>> dnsDaemonRef =
private final AtomicReference<Optional<DNSDaemon>> dnsDaemonRef =
new AtomicReference<>(Optional.empty());
/**
@@ -242,17 +241,16 @@ public class DefaultP2PNetwork implements P2PNetwork {
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<String> 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 class DefaultP2PNetwork implements P2PNetwork {
// 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 class DefaultP2PNetwork implements P2PNetwork {
@VisibleForTesting
Optional<DNSDaemon> getDnsDaemon() {
return dnsDaemonRef.get().map(Pair::getRight);
return dnsDaemonRef.get();
}
@VisibleForTesting

View File

@@ -16,7 +16,6 @@ package org.hyperledger.besu.ethereum.p2p.discovery.dns;
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.bouncycastle.jce.provider.BouncyCastleProvider;
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 @@ import org.junit.jupiter.api.extension.ExtendWith;
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 @@ class DNSDaemonTest {
"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 @@ class DNSDaemonTest {
"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);
}

View File

@@ -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 org.hyperledger.besu.evm.worldstate.WorldView;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.tuweni.bytes.Bytes;
@@ -92,6 +94,7 @@ public interface OperationTracer {
* @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 @@ public interface OperationTracer {
final Bytes output,
final List<Log> logs,
final long gasUsed,
final Set<Address> selfDestructs,
final long timeNs) {}
/**

View File

@@ -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

View File

@@ -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'