mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 21:17:54 -05:00
Merge branch 'main' into zkbesu
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,19 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## Next Release
|
||||
## 24.6.0
|
||||
|
||||
### Breaking Changes
|
||||
- Java 21 has been enforced as minimum version to build and run Besu.
|
||||
- With --Xbonsai-limit-trie-logs-enabled by default in this release, historic trie log data will be removed from the database unless sync-mode=FULL. It respects the --bonsai-historical-block-limit setting so shouldn't break any RPCs, but may be breaking if you are accessing this data from the database directly. Can be disabled with --bonsai-limit-trie-logs-enabled=false
|
||||
- In profile=ENTERPRISE, use sync-mode=FULL (instead of FAST) and data-storage-format=FOREST (instead of BONSAI) [#7186](https://github.com/hyperledger/besu/pull/7186)
|
||||
- If this breaks your node, you can reset sync-mode=FAST and data-storage-format=BONSAI
|
||||
|
||||
### Upcoming Breaking Changes
|
||||
- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version.
|
||||
- PKI-backed QBFT will be removed in a future version of Besu. Other forms of QBFT will remain unchanged.
|
||||
- --Xbonsai-limit-trie-logs-enabled is deprecated, use --bonsai-limit-trie-logs-enabled instead
|
||||
- --Xbonsai-trie-logs-pruning-window-size is deprecated, use --bonsai-trie-logs-pruning-window-size instead
|
||||
- Receipt compaction will be enabled by default in a future version of Besu. After this change it will not be possible to downgrade to the previous Besu version.
|
||||
|
||||
### Additions and Improvements
|
||||
- Add two counters to DefaultBlockchain in order to be able to calculate TPS and Mgas/s [#7105](https://github.com/hyperledger/besu/pull/7105)
|
||||
- Enable --Xbonsai-limit-trie-logs-enabled by default, unless sync-mode=FULL [#7181](https://github.com/hyperledger/besu/pull/7181)
|
||||
- Promote experimental --Xbonsai-limit-trie-logs-enabled to production-ready, --bonsai-limit-trie-logs-enabled [#7192](https://github.com/hyperledger/besu/pull/7192)
|
||||
- Promote experimental --Xbonsai-trie-logs-pruning-window-size to production-ready, --bonsai-trie-logs-pruning-window-size [#7192](https://github.com/hyperledger/besu/pull/7192)
|
||||
- `admin_nodeInfo` JSON/RPC call returns the currently active EVM version [#7127](https://github.com/hyperledger/besu/pull/7127)
|
||||
|
||||
- Improve the selection of the most profitable built block [#7174](https://github.com/hyperledger/besu/pull/7174)
|
||||
- Support for eth_maxPriorityFeePerGas [#5658](https://github.com/hyperledger/besu/issues/5658)
|
||||
### Bug fixes
|
||||
- Make `eth_gasPrice` aware of the base fee market [#7102](https://github.com/hyperledger/besu/pull/7102)
|
||||
|
||||
- Validation errors ignored in accounts-allowlist and empty list [#7138](https://github.com/hyperledger/besu/issues/7138)
|
||||
## 24.5.2
|
||||
|
||||
### Upcoming Breaking Changes
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -24,6 +24,7 @@ solidity {
|
||||
resolvePackages = false
|
||||
// TODO: remove the forced version, when DEV network is upgraded to support latest forks
|
||||
version '0.8.19'
|
||||
evmVersion 'london'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -79,6 +80,7 @@ dependencies {
|
||||
testImplementation 'org.web3j:besu'
|
||||
testImplementation 'org.web3j:core'
|
||||
testImplementation 'org.wiremock:wiremock'
|
||||
testImplementation project(path: ':acceptance-tests:tests:shanghai')
|
||||
|
||||
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
|
||||
}
|
||||
@@ -153,6 +155,7 @@ task acceptanceTestMainnet(type: Test) {
|
||||
task acceptanceTestNotPrivacy(type: Test) {
|
||||
inputs.property "integration.date", LocalTime.now() // so it runs at every invocation
|
||||
exclude '**/privacy/**'
|
||||
exclude '**/bftsoak/**'
|
||||
|
||||
useJUnitPlatform {}
|
||||
|
||||
@@ -205,6 +208,35 @@ task acceptanceTestCliqueBft(type: Test) {
|
||||
doFirst { mkdir "${buildDir}/jvmErrorLogs" }
|
||||
}
|
||||
|
||||
task acceptanceTestBftSoak(type: Test) {
|
||||
inputs.property "integration.date", LocalTime.now() // so it runs at every invocation
|
||||
include '**/bftsoak/**'
|
||||
|
||||
useJUnitPlatform {}
|
||||
|
||||
dependsOn(rootProject.installDist)
|
||||
setSystemProperties(test.getSystemProperties())
|
||||
systemProperty 'acctests.runBesuAsProcess', 'true'
|
||||
// Set to any time > 60 minutes to run the soak test for longer
|
||||
// systemProperty 'acctests.soakTimeMins', '120'
|
||||
systemProperty 'java.security.properties', "${buildDir}/resources/test/acceptanceTesting.security"
|
||||
mustRunAfter rootProject.subprojects*.test
|
||||
description = 'Runs BFT soak test.'
|
||||
group = 'verification'
|
||||
|
||||
jvmArgs "-XX:ErrorFile=${buildDir}/jvmErrorLogs/java_err_pid%p.log"
|
||||
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
showStackTraces = true
|
||||
showStandardStreams = true
|
||||
showExceptions = true
|
||||
showCauses = true
|
||||
}
|
||||
|
||||
doFirst { mkdir "${buildDir}/jvmErrorLogs" }
|
||||
}
|
||||
|
||||
task acceptanceTestPrivacy(type: Test) {
|
||||
inputs.property "integration.date", LocalTime.now() // so it runs at every invocation
|
||||
include '**/privacy/**'
|
||||
|
||||
@@ -19,7 +19,7 @@ import "./EventEmitter.sol";
|
||||
// compile with:
|
||||
// solc CrossContractReader.sol --bin --abi --optimize --overwrite -o .
|
||||
// then create web3j wrappers with:
|
||||
// web3j solidity generate -b ./generated/CrossContractReader.bin -a ./generated/CrossContractReader.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
// web3j generate solidity -b ./generated/CrossContractReader.bin -a ./generated/CrossContractReader.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
contract CrossContractReader {
|
||||
uint counter;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ pragma solidity >=0.7.0 <0.9.0;
|
||||
// compile with:
|
||||
// solc EventEmitter.sol --bin --abi --optimize --overwrite -o .
|
||||
// then create web3j wrappers with:
|
||||
// web3j solidity generate -b ./generated/EventEmitter.bin -a ./generated/EventEmitter.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
// web3j generate solidity -b ./generated/EventEmitter.bin -a ./generated/EventEmitter.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
contract EventEmitter {
|
||||
address owner;
|
||||
event stored(address _to, uint _amount);
|
||||
|
||||
@@ -19,7 +19,7 @@ import "./SimpleStorage.sol";
|
||||
// compile with:
|
||||
// solc RemoteSimpleStorage.sol --bin --abi --optimize --overwrite -o .
|
||||
// then create web3j wrappers with:
|
||||
// web3j solidity generate -b ./generated/RemoteSimpleStorage.bin -a ./generated/RemoteSimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
// web3j generate solidity -b ./generated/RemoteSimpleStorage.bin -a ./generated/RemoteSimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
contract RemoteSimpleStorage {
|
||||
SimpleStorage public simpleStorage;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ pragma solidity >=0.7.0 <0.9.0;
|
||||
// compile with:
|
||||
// solc RevertReason.sol --bin --abi --optimize --overwrite -o .
|
||||
// then create web3j wrappers with:
|
||||
// web3j solidity generate -b ./generated/RevertReason.bin -a ./generated/RevertReason.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
// web3j generate solidity -b ./generated/RevertReason.bin -a ./generated/RevertReason.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
contract RevertReason {
|
||||
|
||||
function revertWithRevertReason() public pure returns (bool) {
|
||||
|
||||
@@ -17,7 +17,7 @@ pragma solidity >=0.7.0 <0.8.20;
|
||||
// compile with:
|
||||
// solc SimpleStorage.sol --bin --abi --optimize --overwrite -o .
|
||||
// then create web3j wrappers with:
|
||||
// web3j solidity generate -b ./generated/SimpleStorage.bin -a ./generated/SimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
// web3j generate solidity -b ./generated/SimpleStorage.bin -a ./generated/SimpleStorage.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
contract SimpleStorage {
|
||||
uint data;
|
||||
|
||||
|
||||
21
acceptance-tests/tests/shanghai/build.gradle
Normal file
21
acceptance-tests/tests/shanghai/build.gradle
Normal 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'
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
pragma solidity >=0.8.20;
|
||||
|
||||
// compile with:
|
||||
// solc SimpleStorageShanghai.sol --bin --abi --optimize --overwrite -o .
|
||||
// then create web3j wrappers with:
|
||||
// web3j generate solidity -b ./SimpleStorageShanghai.bin -a ./SimpleStorageShanghai.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated
|
||||
contract SimpleStorageShanghai {
|
||||
uint data;
|
||||
|
||||
function set(uint value) public {
|
||||
data = value;
|
||||
}
|
||||
|
||||
function get() public view returns (uint) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,14 @@ public class BftAcceptanceTestParameterization {
|
||||
@FunctionalInterface
|
||||
public interface NodeCreator {
|
||||
|
||||
BesuNode create(BesuNodeFactory factory, String name) throws Exception;
|
||||
BesuNode create(BesuNodeFactory factory, String name, boolean fixedPort) throws Exception;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface FixedPortNodeCreator {
|
||||
|
||||
BesuNode createFixedPort(BesuNodeFactory factory, String name, boolean fixedPort)
|
||||
throws Exception;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
@@ -57,7 +64,11 @@ public class BftAcceptanceTestParameterization {
|
||||
}
|
||||
|
||||
public BesuNode createNode(BesuNodeFactory factory, String name) throws Exception {
|
||||
return creatorFn.create(factory, name);
|
||||
return creatorFn.create(factory, name, false);
|
||||
}
|
||||
|
||||
public BesuNode createNodeFixedPort(BesuNodeFactory factory, String name) throws Exception {
|
||||
return creatorFn.create(factory, name, true);
|
||||
}
|
||||
|
||||
public BesuNode createNodeWithValidators(
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.tests.acceptance.bftsoak;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.config.JsonUtil;
|
||||
import org.hyperledger.besu.tests.acceptance.bft.BftAcceptanceTestParameterization;
|
||||
import org.hyperledger.besu.tests.acceptance.bft.ParameterizedBftTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
|
||||
import org.hyperledger.besu.tests.web3j.generated.SimpleStorage;
|
||||
import org.hyperledger.besu.tests.web3j.generated.SimpleStorageShanghai;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class BftMiningSoakTest extends ParameterizedBftTestBase {
|
||||
|
||||
private final int NUM_STEPS = 5;
|
||||
|
||||
private final int MIN_TEST_TIME_MINS = 60;
|
||||
|
||||
private static final long ONE_MINUTE = Duration.of(1, ChronoUnit.MINUTES).toMillis();
|
||||
|
||||
private static final long THREE_MINUTES = Duration.of(1, ChronoUnit.MINUTES).toMillis();
|
||||
|
||||
private static final long TEN_SECONDS = Duration.of(10, ChronoUnit.SECONDS).toMillis();
|
||||
|
||||
static int getTestDurationMins() {
|
||||
// Use a default soak time of 60 mins
|
||||
return Integer.getInteger("acctests.soakTimeMins", 60);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: {0}")
|
||||
@MethodSource("factoryFunctions")
|
||||
public void shouldBeStableDuringLongTest(
|
||||
final String testName, final BftAcceptanceTestParameterization nodeFactory) throws Exception {
|
||||
setUp(testName, nodeFactory);
|
||||
|
||||
// There is a minimum amount of time the test can be run for, due to hard coded delays
|
||||
// in between certain steps. There should be no upper-limit to how long the test is run for
|
||||
assertThat(getTestDurationMins()).isGreaterThanOrEqualTo(MIN_TEST_TIME_MINS);
|
||||
|
||||
final BesuNode minerNode1 = nodeFactory.createNodeFixedPort(besu, "miner1");
|
||||
final BesuNode minerNode2 = nodeFactory.createNodeFixedPort(besu, "miner2");
|
||||
final BesuNode minerNode3 = nodeFactory.createNodeFixedPort(besu, "miner3");
|
||||
final BesuNode minerNode4 = nodeFactory.createNodeFixedPort(besu, "miner4");
|
||||
|
||||
// Each step should be given a minimum of 3 minutes to complete successfully. If the time
|
||||
// give to run the soak test results in a time-per-step lower than this then the time
|
||||
// needs to be increased.
|
||||
assertThat(getTestDurationMins() / NUM_STEPS).isGreaterThanOrEqualTo(3);
|
||||
|
||||
cluster.start(minerNode1, minerNode2, minerNode3, minerNode4);
|
||||
|
||||
cluster.verify(blockchain.reachesHeight(minerNode1, 1, 85));
|
||||
|
||||
// Setup
|
||||
// Deploy a contract that we'll invoke periodically to ensure state
|
||||
// is correct during the test, especially after stopping nodes and
|
||||
// applying new forks.
|
||||
SimpleStorage simpleStorageContract =
|
||||
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorage.class));
|
||||
|
||||
// Check the contract address is as expected for this sender & nonce
|
||||
contractVerifier
|
||||
.validTransactionReceipt("0x42699a7612a82f1d9c36148af9c77354759b210b")
|
||||
.verify(simpleStorageContract);
|
||||
|
||||
// Before upgrading to newer forks, try creating a shanghai-evm contract and check that
|
||||
// the transaction fails
|
||||
try {
|
||||
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorageShanghai.class));
|
||||
Assertions.fail("Shanghai transaction should not be executed on a pre-shanghai chain");
|
||||
} catch (RuntimeException e) {
|
||||
assertThat(e.getMessage())
|
||||
.contains(
|
||||
"Revert reason: 'Transaction processing could not be completed due to an exception'");
|
||||
}
|
||||
|
||||
// Should initially be set to 0
|
||||
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(0));
|
||||
|
||||
// Set to something new
|
||||
simpleStorageContract.set(BigInteger.valueOf(101)).send();
|
||||
|
||||
// Check the state of the contract has updated correctly. We'll set & get this several times
|
||||
// during the test
|
||||
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(101));
|
||||
|
||||
// Step 1
|
||||
// Run for the configured time period, periodically checking that
|
||||
// the chain is progressing as expected
|
||||
BigInteger chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
assertThat(chainHeight.compareTo(BigInteger.ZERO)).isGreaterThanOrEqualTo(1);
|
||||
BigInteger lastChainHeight = chainHeight;
|
||||
|
||||
Instant startTime = Instant.now();
|
||||
Instant nextStepEndTime = startTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
|
||||
|
||||
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
|
||||
Thread.sleep(ONE_MINUTE);
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
|
||||
// With 1-second block period chain height should have moved on by at least 50 blocks
|
||||
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(50))))
|
||||
.isGreaterThanOrEqualTo(1);
|
||||
lastChainHeight = chainHeight;
|
||||
}
|
||||
Instant previousStepEndTime = Instant.now();
|
||||
|
||||
// Step 2
|
||||
// Stop one of the nodes, check that the chain continues mining
|
||||
// blocks
|
||||
stopNode(minerNode4);
|
||||
|
||||
nextStepEndTime =
|
||||
previousStepEndTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
|
||||
|
||||
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
|
||||
Thread.sleep(ONE_MINUTE);
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
|
||||
// Chain height should have moved on by at least 5 blocks
|
||||
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(20))))
|
||||
.isGreaterThanOrEqualTo(1);
|
||||
lastChainHeight = chainHeight;
|
||||
}
|
||||
previousStepEndTime = Instant.now();
|
||||
|
||||
// Step 3
|
||||
// Stop another one of the nodes, check that the chain now stops
|
||||
// mining blocks
|
||||
stopNode(minerNode3);
|
||||
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
lastChainHeight = chainHeight;
|
||||
|
||||
// Leave the chain stalled for 3 minutes. Check no new blocks are mined. Then
|
||||
// resume the other validators.
|
||||
nextStepEndTime = previousStepEndTime.plus(3, ChronoUnit.MINUTES);
|
||||
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
|
||||
Thread.sleep(ONE_MINUTE);
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
|
||||
// Chain height should not have moved on
|
||||
assertThat(chainHeight.equals(lastChainHeight)).isTrue();
|
||||
}
|
||||
|
||||
// Step 4
|
||||
// Restart both of the stopped nodes. Check that the chain resumes
|
||||
// mining blocks
|
||||
startNode(minerNode3);
|
||||
|
||||
startNode(minerNode4);
|
||||
|
||||
previousStepEndTime = Instant.now();
|
||||
|
||||
// This step gives the stalled chain time to re-sync and agree on the next BFT round
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
nextStepEndTime =
|
||||
previousStepEndTime.plus((getTestDurationMins() / NUM_STEPS), ChronoUnit.MINUTES);
|
||||
lastChainHeight = chainHeight;
|
||||
|
||||
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
|
||||
Thread.sleep(ONE_MINUTE);
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
lastChainHeight = chainHeight;
|
||||
}
|
||||
previousStepEndTime = Instant.now();
|
||||
|
||||
// By this loop it should be producing blocks again
|
||||
nextStepEndTime =
|
||||
previousStepEndTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
|
||||
|
||||
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
|
||||
Thread.sleep(ONE_MINUTE);
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
|
||||
// Chain height should have moved on by at least 1 block
|
||||
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(1))))
|
||||
.isGreaterThanOrEqualTo(1);
|
||||
lastChainHeight = chainHeight;
|
||||
}
|
||||
|
||||
// Update our smart contract before upgrading from berlin to london
|
||||
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(101));
|
||||
simpleStorageContract.set(BigInteger.valueOf(201)).send();
|
||||
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(201));
|
||||
|
||||
// Upgrade the chain from berlin to london in 120 blocks time
|
||||
upgradeToLondon(
|
||||
minerNode1, minerNode2, minerNode3, minerNode4, lastChainHeight.intValue() + 120);
|
||||
|
||||
previousStepEndTime = Instant.now();
|
||||
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
nextStepEndTime =
|
||||
previousStepEndTime.plus(getTestDurationMins() / NUM_STEPS, ChronoUnit.MINUTES);
|
||||
lastChainHeight = chainHeight;
|
||||
|
||||
while (System.currentTimeMillis() < nextStepEndTime.toEpochMilli()) {
|
||||
Thread.sleep(ONE_MINUTE);
|
||||
chainHeight = minerNode1.execute(ethTransactions.blockNumber());
|
||||
|
||||
// Chain height should have moved on by at least 50 blocks
|
||||
assertThat(chainHeight.compareTo(lastChainHeight.add(BigInteger.valueOf(50))))
|
||||
.isGreaterThanOrEqualTo(1);
|
||||
lastChainHeight = chainHeight;
|
||||
}
|
||||
|
||||
// Check that the state of our smart contract is still correct
|
||||
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(201));
|
||||
|
||||
// Update it once more to check new transactions are mined OK
|
||||
simpleStorageContract.set(BigInteger.valueOf(301)).send();
|
||||
assertThat(simpleStorageContract.get().send()).isEqualTo(BigInteger.valueOf(301));
|
||||
|
||||
// Upgrade the chain to shanghai in 120 seconds. Then try to deploy a shanghai contract
|
||||
upgradeToShanghai(
|
||||
minerNode1, minerNode2, minerNode3, minerNode4, Instant.now().getEpochSecond() + 120);
|
||||
|
||||
Thread.sleep(THREE_MINUTES);
|
||||
|
||||
SimpleStorageShanghai simpleStorageContractShanghai =
|
||||
minerNode1.execute(contractTransactions.createSmartContract(SimpleStorageShanghai.class));
|
||||
|
||||
// Check the contract address is as expected for this sender & nonce
|
||||
contractVerifier
|
||||
.validTransactionReceipt("0x05d91b9031a655d08e654177336d08543ac4b711")
|
||||
.verify(simpleStorageContractShanghai);
|
||||
}
|
||||
|
||||
private static void updateGenesisConfigToLondon(
|
||||
final BesuNode minerNode, final boolean zeroBaseFeeEnabled, final int blockNumber) {
|
||||
|
||||
if (minerNode.getGenesisConfig().isPresent()) {
|
||||
final ObjectNode genesisConfigNode =
|
||||
JsonUtil.objectNodeFromString(minerNode.getGenesisConfig().get());
|
||||
final ObjectNode config = (ObjectNode) genesisConfigNode.get("config");
|
||||
config.put("londonBlock", blockNumber);
|
||||
config.put("zeroBaseFee", zeroBaseFeeEnabled);
|
||||
minerNode.setGenesisConfig(genesisConfigNode.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateGenesisConfigToShanghai(
|
||||
final BesuNode minerNode, final long blockTimestamp) {
|
||||
|
||||
if (minerNode.getGenesisConfig().isPresent()) {
|
||||
final ObjectNode genesisConfigNode =
|
||||
JsonUtil.objectNodeFromString(minerNode.getGenesisConfig().get());
|
||||
final ObjectNode config = (ObjectNode) genesisConfigNode.get("config");
|
||||
config.put("shanghaiTime", blockTimestamp);
|
||||
minerNode.setGenesisConfig(genesisConfigNode.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void upgradeToLondon(
|
||||
final BesuNode minerNode1,
|
||||
final BesuNode minerNode2,
|
||||
final BesuNode minerNode3,
|
||||
final BesuNode minerNode4,
|
||||
final int londonBlockNumber)
|
||||
throws InterruptedException {
|
||||
// Node 1
|
||||
stopNode(minerNode1);
|
||||
updateGenesisConfigToLondon(minerNode1, true, londonBlockNumber);
|
||||
startNode(minerNode1);
|
||||
|
||||
// Node 2
|
||||
stopNode(minerNode2);
|
||||
updateGenesisConfigToLondon(minerNode2, true, londonBlockNumber);
|
||||
startNode(minerNode2);
|
||||
|
||||
// Node 3
|
||||
stopNode(minerNode3);
|
||||
updateGenesisConfigToLondon(minerNode3, true, londonBlockNumber);
|
||||
startNode(minerNode3);
|
||||
|
||||
// Node 4
|
||||
stopNode(minerNode4);
|
||||
updateGenesisConfigToLondon(minerNode4, true, londonBlockNumber);
|
||||
startNode(minerNode4);
|
||||
}
|
||||
|
||||
private void upgradeToShanghai(
|
||||
final BesuNode minerNode1,
|
||||
final BesuNode minerNode2,
|
||||
final BesuNode minerNode3,
|
||||
final BesuNode minerNode4,
|
||||
final long shanghaiTime)
|
||||
throws InterruptedException {
|
||||
// Node 1
|
||||
stopNode(minerNode1);
|
||||
updateGenesisConfigToShanghai(minerNode1, shanghaiTime);
|
||||
startNode(minerNode1);
|
||||
|
||||
// Node 2
|
||||
stopNode(minerNode2);
|
||||
updateGenesisConfigToShanghai(minerNode2, shanghaiTime);
|
||||
startNode(minerNode2);
|
||||
|
||||
// Node 3
|
||||
stopNode(minerNode3);
|
||||
updateGenesisConfigToShanghai(minerNode3, shanghaiTime);
|
||||
startNode(minerNode3);
|
||||
|
||||
// Node 4
|
||||
stopNode(minerNode4);
|
||||
updateGenesisConfigToShanghai(minerNode4, shanghaiTime);
|
||||
startNode(minerNode4);
|
||||
}
|
||||
|
||||
// Start a node with a delay before returning to give it time to start
|
||||
private void startNode(final BesuNode node) throws InterruptedException {
|
||||
cluster.startNode(node);
|
||||
Thread.sleep(TEN_SECONDS);
|
||||
}
|
||||
|
||||
// Stop a node with a delay before returning to give it time to stop
|
||||
private void stopNode(final BesuNode node) throws InterruptedException {
|
||||
cluster.stopNode(node);
|
||||
Thread.sleep(TEN_SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDownAcceptanceTestBase() {
|
||||
cluster.stop();
|
||||
super.tearDownAcceptanceTestBase();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,3 @@
|
||||
sync-mode="CHECKPOINT"
|
||||
data-storage-format="BONSAI"
|
||||
bonsai-historical-block-limit=128
|
||||
max-peers=25
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -88,7 +88,6 @@ public class DNSDaemon extends AbstractVerticle {
|
||||
public void stop() {
|
||||
LOG.info("Stopping DNSDaemon for {}", enrLink);
|
||||
periodicTaskId.ifPresent(vertx::cancelTimer);
|
||||
dnsResolver.close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
/**
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user