mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 21:38:15 -05:00
Merge branch 'main' into zkbesu
This commit is contained in:
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,6 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
## Unreleased
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Upcoming Breaking Changes
|
||||
- Plugin API will be deprecating the BesuContext interface to be replaced with the ServiceManager interface.
|
||||
- `MetricSystem::createLabelledGauge` is deprecated and will be removed in a future release, replace it with `MetricSystem::createLabelledSuppliedGauge`
|
||||
- k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release
|
||||
- `--host-whitelist` has been deprecated in favor of `--host-allowlist` since 2020 and will be removed in a future release
|
||||
- Sunsetting features - for more context on the reasoning behind the deprecation of these features, including alternative options, read [this blog post](https://www.lfdecentralizedtrust.org/blog/sunsetting-tessera-and-simplifying-hyperledger-besu)
|
||||
- Tessera privacy
|
||||
- Smart-contract-based (onchain) permissioning
|
||||
- Proof of Work consensus
|
||||
- Fast Sync
|
||||
|
||||
### Additions and Improvements
|
||||
- Proper support for `pending` block tag when calling `eth_estimateGas` and `eth_createAccessList` [#7951](https://github.com/hyperledger/besu/pull/7951)
|
||||
|
||||
### Bug fixes
|
||||
- Correct default parameters for frontier transactions in `eth_call` and `eth_estimateGas` [#7965](https://github.com/hyperledger/besu/pull/7965)
|
||||
|
||||
## 24.12.0
|
||||
|
||||
### Breaking Changes
|
||||
- Removed Retesteth rpc service and commands [#7833](https://github.com/hyperledger/besu/pull/7783)
|
||||
@@ -43,8 +64,10 @@
|
||||
- Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832)
|
||||
- Add a new metric collector for counters which get their value from suppliers [#7894](https://github.com/hyperledger/besu/pull/7894)
|
||||
- Add account and state overrides to `eth_call` [#7801](https://github.com/hyperledger/besu/pull/7801) and `eth_estimateGas` [#7890](https://github.com/hyperledger/besu/pull/7890)
|
||||
- Add RPC WS options to specify password file for keystore and truststore [#7970](https://github.com/hyperledger/besu/pull/7970)
|
||||
- Prometheus Java Metrics library upgraded to version 1.3.3 [#7880](https://github.com/hyperledger/besu/pull/7880)
|
||||
- Add histogram to Prometheus metrics system [#7944](https://github.com/hyperledger/besu/pull/7944)
|
||||
- Improve newPayload and FCU logs [#7961](https://github.com/hyperledger/besu/pull/7961)
|
||||
|
||||
### Bug fixes
|
||||
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)
|
||||
|
||||
@@ -11,6 +11,9 @@ Having the following accounts is necessary for contributing code/issues to Besu.
|
||||
* To ask questions or chat with us, join our [Discord](https://discord.com/invite/hyperledger)
|
||||
* To edit pages in our wiki, you'll need a [Linux Foundation (LF) account].
|
||||
|
||||
### Guidelines for Non-Code and other Trivial Contributions
|
||||
Please keep in mind that we do not accept non-code contributions like fixing comments, typos or some other trivial fixes. Although we appreciate the extra help, managing lots of these small contributions is unfeasible, and puts extra pressure in our continuous delivery systems (running all tests, etc). Feel free to open an issue pointing any of those errors, and we will batch them into a single change.
|
||||
|
||||
### Useful contributing links
|
||||
|
||||
* [I just have a quick question](https://wiki.hyperledger.org/display/BESU/I+just+have+a+quick+question)
|
||||
|
||||
@@ -396,9 +396,14 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final ProtocolSchedule protocolSchedule,
|
||||
final MiningConfiguration miningConfiguration,
|
||||
final ApiConfiguration apiConfiguration) {
|
||||
return new TransactionSimulator(
|
||||
blockchain, worldStateArchive, protocolSchedule, apiConfiguration.getGasCap());
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
protocolSchedule,
|
||||
miningConfiguration,
|
||||
apiConfiguration.getGasCap());
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -120,6 +120,11 @@ public class CliqueMiningAcceptanceTest extends AcceptanceTestBase {
|
||||
final BesuNode minerNode1, final BesuNode minerNode2, final BesuNode minerNode3) {
|
||||
cluster.start(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
// verify nodes are fully connected otherwise blocks could not be propagated
|
||||
minerNode1.verify(net.awaitPeerCount(2));
|
||||
minerNode2.verify(net.awaitPeerCount(2));
|
||||
minerNode3.verify(net.awaitPeerCount(2));
|
||||
|
||||
// verify that we have started producing blocks
|
||||
waitForBlockHeight(minerNode1, 1);
|
||||
final var minerChainHead = minerNode1.execute(ethTransactions.block());
|
||||
|
||||
@@ -33,6 +33,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
|
||||
final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators);
|
||||
cluster.start(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
|
||||
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
|
||||
minerNode2.execute(cliqueTransactions.createAddProposal(minerNode3));
|
||||
@@ -47,6 +49,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
|
||||
final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators);
|
||||
cluster.start(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3));
|
||||
final Condition cliqueValidatorsChanged = clique.awaitSignerSetChange(minerNode1);
|
||||
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode3));
|
||||
@@ -63,6 +67,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
|
||||
final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators);
|
||||
cluster.start(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2));
|
||||
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
|
||||
minerNode1.verify(blockchain.reachesHeight(minerNode1, 1));
|
||||
@@ -76,6 +82,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
|
||||
final BesuNode minerNode3 = besu.createCliqueNode("miner3");
|
||||
cluster.start(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
cluster.verify(clique.validatorsEqual(minerNode1, minerNode2, minerNode3));
|
||||
minerNode1.execute(cliqueTransactions.createRemoveProposal(minerNode3));
|
||||
minerNode1.verify(blockchain.reachesHeight(minerNode1, 1));
|
||||
@@ -90,6 +98,8 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
|
||||
final BesuNode minerNode3 = besu.createCliqueNodeWithValidators("miner3", initialValidators);
|
||||
cluster.start(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
waitForNodesConnectedAndInSync(minerNode1, minerNode2, minerNode3);
|
||||
|
||||
minerNode1.execute(cliqueTransactions.createAddProposal(minerNode3));
|
||||
minerNode1.verify(blockchain.reachesHeight(minerNode1, 1));
|
||||
minerNode1.verify(blockchain.beneficiaryEquals(minerNode3));
|
||||
@@ -100,4 +110,18 @@ public class CliqueProposeRpcAcceptanceTest extends AcceptanceTestBase {
|
||||
minerNode1.verify(blockchain.beneficiaryEquals(minerNode2));
|
||||
minerNode1.verify(clique.nonceVoteEquals(CLIQUE_NONCE_VOTE.DROP));
|
||||
}
|
||||
|
||||
private void waitForNodesConnectedAndInSync(
|
||||
final BesuNode minerNode1, final BesuNode minerNode2, final BesuNode minerNode3) {
|
||||
// verify nodes are fully connected otherwise blocks could not be propagated
|
||||
minerNode1.verify(net.awaitPeerCount(2));
|
||||
minerNode2.verify(net.awaitPeerCount(2));
|
||||
minerNode3.verify(net.awaitPeerCount(2));
|
||||
|
||||
// verify that the miner started producing blocks and all other nodes are syncing from it
|
||||
waitForBlockHeight(minerNode1, 1);
|
||||
final var minerChainHead = minerNode1.execute(ethTransactions.block());
|
||||
minerNode2.verify(blockchain.minimumHeight(minerChainHead.getNumber().longValue()));
|
||||
minerNode3.verify(blockchain.minimumHeight(minerChainHead.getNumber().longValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,35 @@ import picocli.CommandLine;
|
||||
|
||||
/** This class represents the WebSocket options for the RPC. */
|
||||
public class RpcWebsocketOptions {
|
||||
|
||||
static class KeystorePasswordOptions {
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-keystore-password"},
|
||||
paramLabel = "<PASSWORD>",
|
||||
description = "Password for the WebSocket RPC keystore file")
|
||||
private String rpcWsKeyStorePassword;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-keystore-password-file"},
|
||||
paramLabel = "<FILE>",
|
||||
description = "File containing the password for WebSocket keystore.")
|
||||
private String rpcWsKeystorePasswordFile;
|
||||
}
|
||||
|
||||
static class TruststorePasswordOptions {
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-truststore-password"},
|
||||
paramLabel = "<PASSWORD>",
|
||||
description = "Password for the WebSocket RPC truststore file")
|
||||
private String rpcWsTrustStorePassword;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-truststore-password-file"},
|
||||
paramLabel = "<FILE>",
|
||||
description = "File containing the password for WebSocket truststore.")
|
||||
private String rpcWsTruststorePasswordFile;
|
||||
}
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-authentication-jwt-algorithm"},
|
||||
description =
|
||||
@@ -131,12 +160,6 @@ public class RpcWebsocketOptions {
|
||||
description = "Path to the keystore file for the WebSocket RPC service")
|
||||
private String rpcWsKeyStoreFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-keystore-password"},
|
||||
paramLabel = "<PASSWORD>",
|
||||
description = "Password for the WebSocket RPC keystore file")
|
||||
private String rpcWsKeyStorePassword = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-key-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
@@ -167,12 +190,6 @@ public class RpcWebsocketOptions {
|
||||
description = "Path to the truststore file for the WebSocket RPC service")
|
||||
private String rpcWsTrustStoreFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-truststore-password"},
|
||||
paramLabel = "<PASSWORD>",
|
||||
description = "Password for the WebSocket RPC truststore file")
|
||||
private String rpcWsTrustStorePassword = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-trustcert-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
@@ -185,6 +202,12 @@ public class RpcWebsocketOptions {
|
||||
description = "Type of the truststore (JKS, PKCS12, PEM)")
|
||||
private String rpcWsTrustStoreType = null;
|
||||
|
||||
@CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
|
||||
private KeystorePasswordOptions keystorePasswordOptions;
|
||||
|
||||
@CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
|
||||
private TruststorePasswordOptions truststorePasswordOptions;
|
||||
|
||||
/** Default Constructor. */
|
||||
public RpcWebsocketOptions() {}
|
||||
|
||||
@@ -292,7 +315,7 @@ public class RpcWebsocketOptions {
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-keystore-file",
|
||||
rpcWsKeyStoreFile == null,
|
||||
List.of("--rpc-ws-ssl-keystore-password"));
|
||||
List.of("--rpc-ws-ssl-keystore-password", "--rpc-ws-ssl-keystore-password-file"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +325,7 @@ public class RpcWebsocketOptions {
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-truststore-file",
|
||||
rpcWsTrustStoreFile == null,
|
||||
List.of("--rpc-ws-ssl-truststore-password"));
|
||||
List.of("--rpc-ws-ssl-truststore-password", "--rpc-ws-ssl-truststore-password-file"));
|
||||
}
|
||||
|
||||
if (isRpcWsAuthenticationEnabled) {
|
||||
@@ -343,16 +366,27 @@ public class RpcWebsocketOptions {
|
||||
webSocketConfiguration.setTimeoutSec(wsTimoutSec);
|
||||
webSocketConfiguration.setSslEnabled(isRpcWsSslEnabled);
|
||||
webSocketConfiguration.setKeyStorePath(rpcWsKeyStoreFile);
|
||||
webSocketConfiguration.setKeyStorePassword(rpcWsKeyStorePassword);
|
||||
webSocketConfiguration.setKeyStoreType(rpcWsKeyStoreType);
|
||||
webSocketConfiguration.setClientAuthEnabled(isRpcWsClientAuthEnabled);
|
||||
webSocketConfiguration.setTrustStorePath(rpcWsTrustStoreFile);
|
||||
webSocketConfiguration.setTrustStorePassword(rpcWsTrustStorePassword);
|
||||
webSocketConfiguration.setTrustStoreType(rpcWsTrustStoreType);
|
||||
webSocketConfiguration.setKeyPath(rpcWsKeyFile);
|
||||
webSocketConfiguration.setCertPath(rpcWsCertFile);
|
||||
webSocketConfiguration.setTrustCertPath(rpcWsTrustCertFile);
|
||||
|
||||
if (keystorePasswordOptions != null) {
|
||||
webSocketConfiguration.setKeyStorePassword(keystorePasswordOptions.rpcWsKeyStorePassword);
|
||||
webSocketConfiguration.setKeyStorePasswordFile(
|
||||
keystorePasswordOptions.rpcWsKeystorePasswordFile);
|
||||
}
|
||||
|
||||
if (truststorePasswordOptions != null) {
|
||||
webSocketConfiguration.setTrustStorePassword(
|
||||
truststorePasswordOptions.rpcWsTrustStorePassword);
|
||||
webSocketConfiguration.setTrustStorePasswordFile(
|
||||
truststorePasswordOptions.rpcWsTruststorePasswordFile);
|
||||
}
|
||||
|
||||
return webSocketConfiguration;
|
||||
}
|
||||
|
||||
|
||||
@@ -626,7 +626,11 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
|
||||
transactionSimulator =
|
||||
new TransactionSimulator(
|
||||
blockchain, worldStateArchive, protocolSchedule, apiConfiguration.getGasCap());
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
protocolSchedule,
|
||||
miningConfiguration,
|
||||
apiConfiguration.getGasCap());
|
||||
|
||||
final var consensusContext =
|
||||
createConsensusContext(blockchain, worldStateArchive, protocolSchedule);
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.AccountOverrideMap;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
@@ -35,11 +34,6 @@ import java.util.Optional;
|
||||
/** TransactionSimulationServiceImpl */
|
||||
@Unstable
|
||||
public class TransactionSimulationServiceImpl implements TransactionSimulationService {
|
||||
private static final TransactionValidationParams SIMULATOR_ALLOWING_EXCEEDING_BALANCE =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(true)
|
||||
.build();
|
||||
private Blockchain blockchain;
|
||||
private TransactionSimulator transactionSimulator;
|
||||
|
||||
@@ -57,46 +51,50 @@ public class TransactionSimulationServiceImpl implements TransactionSimulationSe
|
||||
this.transactionSimulator = transactionSimulator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TransactionSimulationResult> simulate(
|
||||
final Transaction transaction,
|
||||
final Hash blockHash,
|
||||
final OperationTracer operationTracer,
|
||||
final boolean isAllowExceedingBalance) {
|
||||
return simulate(
|
||||
transaction, Optional.empty(), blockHash, operationTracer, isAllowExceedingBalance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TransactionSimulationResult> simulate(
|
||||
final Transaction transaction,
|
||||
final Optional<AccountOverrideMap> maybeAccountOverrides,
|
||||
final Hash blockHash,
|
||||
final Optional<Hash> maybeBlockHash,
|
||||
final OperationTracer operationTracer,
|
||||
final boolean isAllowExceedingBalance) {
|
||||
|
||||
final CallParameter callParameter = CallParameter.fromTransaction(transaction);
|
||||
|
||||
final var maybeBlockHeader =
|
||||
blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash));
|
||||
if (maybeBlockHash.isPresent()) {
|
||||
final Hash blockHash = maybeBlockHash.get();
|
||||
|
||||
if (maybeBlockHeader.isEmpty()) {
|
||||
return Optional.of(
|
||||
new TransactionSimulationResult(
|
||||
transaction,
|
||||
TransactionProcessingResult.invalid(
|
||||
ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND))));
|
||||
final var maybeBlockHeader =
|
||||
blockchain.getBlockHeader(blockHash).or(() -> blockchain.getBlockHeaderSafe(blockHash));
|
||||
|
||||
if (maybeBlockHeader.isEmpty()) {
|
||||
return Optional.of(
|
||||
new TransactionSimulationResult(
|
||||
transaction,
|
||||
TransactionProcessingResult.invalid(
|
||||
ValidationResult.invalid(TransactionInvalidReason.BLOCK_NOT_FOUND))));
|
||||
}
|
||||
|
||||
return transactionSimulator
|
||||
.process(
|
||||
callParameter,
|
||||
isAllowExceedingBalance
|
||||
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
|
||||
: TransactionValidationParams.transactionSimulator(),
|
||||
operationTracer,
|
||||
maybeBlockHeader.get())
|
||||
.map(res -> new TransactionSimulationResult(transaction, res.result()));
|
||||
}
|
||||
|
||||
return transactionSimulator
|
||||
.process(
|
||||
.processOnPending(
|
||||
callParameter,
|
||||
maybeAccountOverrides,
|
||||
isAllowExceedingBalance
|
||||
? SIMULATOR_ALLOWING_EXCEEDING_BALANCE
|
||||
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
|
||||
: TransactionValidationParams.transactionSimulator(),
|
||||
operationTracer,
|
||||
maybeBlockHeader.get())
|
||||
transactionSimulator.simulatePendingBlockHeader())
|
||||
.map(res -> new TransactionSimulationResult(transaction, res.result()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,10 +123,12 @@ rpc-ws-authentication-jwt-public-key-file="none"
|
||||
rpc-ws-ssl-enabled=false
|
||||
rpc-ws-ssl-keystore-file="none.pfx"
|
||||
rpc-ws-ssl-keystore-password="none.passwd"
|
||||
rpc-ws-ssl-keystore-password-file="none.txt"
|
||||
rpc-ws-ssl-keystore-type="none"
|
||||
rpc-ws-ssl-client-auth-enabled=false
|
||||
rpc-ws-ssl-truststore-file="none.pfx"
|
||||
rpc-ws-ssl-truststore-password="none.passwd"
|
||||
rpc-ws-ssl-truststore-password-file="none.txt"
|
||||
rpc-ws-ssl-truststore-type="none"
|
||||
rpc-ws-ssl-key-file="none.pfx"
|
||||
rpc-ws-ssl-cert-file="none.pfx"
|
||||
|
||||
@@ -154,7 +154,7 @@ public class CliqueMinerExecutorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraDataForNonEpochBlocksDoesNotContainValidaors() {
|
||||
public void extraDataForNonEpochBlocksDoesNotContainValidators() {
|
||||
final Bytes vanityData = generateRandomVanityData();
|
||||
|
||||
final MiningConfiguration miningConfiguration = createMiningConfiguration(vanityData);
|
||||
|
||||
@@ -387,7 +387,7 @@ public class TestContextBuilder {
|
||||
final boolean useFixedBaseFee,
|
||||
final List<QbftFork> qbftForks) {
|
||||
|
||||
final MiningConfiguration miningParams =
|
||||
final MiningConfiguration miningConfiguration =
|
||||
ImmutableMiningConfiguration.builder()
|
||||
.mutableInitValues(
|
||||
MutableInitValues.builder()
|
||||
@@ -445,7 +445,8 @@ public class TestContextBuilder {
|
||||
|
||||
final BftValidatorOverrides validatorOverrides = convertBftForks(qbftForks);
|
||||
final TransactionSimulator transactionSimulator =
|
||||
new TransactionSimulator(blockChain, worldStateArchive, protocolSchedule, 0L);
|
||||
new TransactionSimulator(
|
||||
blockChain, worldStateArchive, protocolSchedule, miningConfiguration, 0L);
|
||||
|
||||
final BlockValidatorProvider blockValidatorProvider =
|
||||
BlockValidatorProvider.forkingValidatorProvider(
|
||||
@@ -496,7 +497,7 @@ public class TestContextBuilder {
|
||||
protocolContext,
|
||||
protocolSchedule,
|
||||
forksSchedule,
|
||||
miningParams,
|
||||
miningConfiguration,
|
||||
localAddress,
|
||||
BFT_EXTRA_DATA_ENCODER,
|
||||
ethScheduler);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package org.hyperledger.besu.consensus.qbft.validator;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
@@ -94,10 +93,7 @@ public class ValidatorContractController {
|
||||
final CallParameter callParams =
|
||||
new CallParameter(null, contractAddress, -1, null, null, payload);
|
||||
final TransactionValidationParams transactionValidationParams =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(true)
|
||||
.build();
|
||||
TransactionValidationParams.transactionSimulatorAllowExceedingBalance();
|
||||
return transactionSimulator.process(
|
||||
callParams, transactionValidationParams, OperationTracer.NO_TRACING, blockNumber);
|
||||
}
|
||||
|
||||
@@ -118,4 +118,16 @@ public class Hash extends DelegatingBytes32 {
|
||||
public static Hash fromHexStringLenient(final String str) {
|
||||
return new Hash(Bytes32.fromHexStringLenient(str));
|
||||
}
|
||||
|
||||
/***
|
||||
* For logging purposes, this method returns a shortened hex representation
|
||||
*
|
||||
* @return shortened string with only the beginning and the end of the hex representation
|
||||
*/
|
||||
public String toShortLogString() {
|
||||
final var hexRepresentation = toFastHex(false);
|
||||
String firstPart = hexRepresentation.substring(0, 5);
|
||||
String lastPart = hexRepresentation.substring(hexRepresentation.length() - 5);
|
||||
return firstPart + "....." + lastPart;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +161,31 @@ public final class Wei extends BaseUInt256Value<Wei> implements Quantity {
|
||||
* @return the string
|
||||
*/
|
||||
public String toHumanReadableString() {
|
||||
return toHumanReadableStringWithPadding(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wei to human-readable string, with padding
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String toHumanReadablePaddedString() {
|
||||
return toHumanReadableStringWithPadding(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable String, the number of returned characters depends on the width
|
||||
* parameter
|
||||
*
|
||||
* @param width the number of digits to use
|
||||
* @return a human-readable String
|
||||
*/
|
||||
private String toHumanReadableStringWithPadding(final int width) {
|
||||
final BigInteger amount = toBigInteger();
|
||||
final int numOfDigits = amount.toString().length();
|
||||
final Unit preferredUnit = Unit.getPreferred(numOfDigits);
|
||||
final double res = amount.doubleValue() / preferredUnit.divisor;
|
||||
return String.format("%1." + preferredUnit.decimals + "f %s", res, preferredUnit);
|
||||
return String.format("%" + width + "." + preferredUnit.decimals + "f %s", res, preferredUnit);
|
||||
}
|
||||
|
||||
/** The enum Unit. */
|
||||
|
||||
@@ -44,4 +44,28 @@ public class WeiTest {
|
||||
assertThat(Wei.of(new BigInteger("1" + String.valueOf(manyZeros))).toHumanReadableString())
|
||||
.isEqualTo("100.00 tether");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toHumanReadablePaddedString() {
|
||||
assertThat(Wei.ZERO.toHumanReadablePaddedString()).isEqualTo(" 0 wei");
|
||||
assertThat(Wei.ONE.toHumanReadablePaddedString()).isEqualTo(" 1 wei");
|
||||
|
||||
assertThat(Wei.of(999).toHumanReadablePaddedString()).isEqualTo(" 999 wei");
|
||||
assertThat(Wei.of(1000).toHumanReadablePaddedString()).isEqualTo(" 1.00 kwei");
|
||||
|
||||
assertThat(Wei.of(1009).toHumanReadablePaddedString()).isEqualTo(" 1.01 kwei");
|
||||
assertThat(Wei.of(1011).toHumanReadablePaddedString()).isEqualTo(" 1.01 kwei");
|
||||
|
||||
assertThat(Wei.of(new BigInteger("1000000000")).toHumanReadablePaddedString())
|
||||
.isEqualTo(" 1.00 gwei");
|
||||
|
||||
assertThat(Wei.of(new BigInteger("1000000000000000000")).toHumanReadablePaddedString())
|
||||
.isEqualTo(" 1.00 ether");
|
||||
|
||||
final char[] manyZeros = new char[32];
|
||||
Arrays.fill(manyZeros, '0');
|
||||
assertThat(
|
||||
Wei.of(new BigInteger("1" + String.valueOf(manyZeros))).toHumanReadablePaddedString())
|
||||
.isEqualTo("100.00 tether");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,12 +97,13 @@ public class JsonRpcTestMethodsFactory {
|
||||
final BlockImporter blockImporter = protocolSpec.getBlockImporter();
|
||||
blockImporter.importBlock(context, block, HeaderValidationMode.FULL);
|
||||
}
|
||||
final var miningConfiguration = MiningConfiguration.newDefault();
|
||||
this.blockchainQueries =
|
||||
new BlockchainQueries(
|
||||
protocolSchedule, blockchain, stateArchive, MiningConfiguration.newDefault());
|
||||
new BlockchainQueries(protocolSchedule, blockchain, stateArchive, miningConfiguration);
|
||||
|
||||
this.transactionSimulator =
|
||||
new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L);
|
||||
new TransactionSimulator(
|
||||
blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L);
|
||||
}
|
||||
|
||||
public JsonRpcTestMethodsFactory(
|
||||
@@ -115,15 +116,14 @@ public class JsonRpcTestMethodsFactory {
|
||||
this.stateArchive = stateArchive;
|
||||
this.context = context;
|
||||
this.protocolSchedule = importer.getProtocolSchedule();
|
||||
final var miningConfiguration = MiningConfiguration.newDefault();
|
||||
this.blockchainQueries =
|
||||
new BlockchainQueries(
|
||||
importer.getProtocolSchedule(),
|
||||
blockchain,
|
||||
stateArchive,
|
||||
MiningConfiguration.newDefault());
|
||||
importer.getProtocolSchedule(), blockchain, stateArchive, miningConfiguration);
|
||||
this.synchronizer = mock(Synchronizer.class);
|
||||
this.transactionSimulator =
|
||||
new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L);
|
||||
new TransactionSimulator(
|
||||
blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L);
|
||||
}
|
||||
|
||||
public JsonRpcTestMethodsFactory(
|
||||
@@ -138,14 +138,13 @@ public class JsonRpcTestMethodsFactory {
|
||||
this.context = context;
|
||||
this.synchronizer = synchronizer;
|
||||
this.protocolSchedule = importer.getProtocolSchedule();
|
||||
final var miningConfiguration = MiningConfiguration.newDefault();
|
||||
this.blockchainQueries =
|
||||
new BlockchainQueries(
|
||||
importer.getProtocolSchedule(),
|
||||
blockchain,
|
||||
stateArchive,
|
||||
MiningConfiguration.newDefault());
|
||||
importer.getProtocolSchedule(), blockchain, stateArchive, miningConfiguration);
|
||||
this.transactionSimulator =
|
||||
new TransactionSimulator(blockchain, stateArchive, protocolSchedule, 0L);
|
||||
new TransactionSimulator(
|
||||
blockchain, stateArchive, protocolSchedule, miningConfiguration, 0L);
|
||||
}
|
||||
|
||||
public BlockchainQueries getBlockchainQueries() {
|
||||
|
||||
@@ -137,6 +137,7 @@ public class EthEstimateGasIntegrationTest {
|
||||
.withChainId(BLOCKCHAIN.getChainId().add(BigInteger.ONE))
|
||||
.withFrom(Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"))
|
||||
.withTo(Address.fromHexString("0x8888f1f195afa192cfee860698584c030f4c9db1"))
|
||||
.withMaxFeePerGas(Wei.ONE)
|
||||
.withValue(Wei.ONE)
|
||||
.build();
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.LogWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
@@ -359,14 +358,9 @@ public class BlockAdapterBase extends AdapterBase {
|
||||
data,
|
||||
Optional.empty());
|
||||
|
||||
ImmutableTransactionValidationParams.Builder transactionValidationParams =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator());
|
||||
transactionValidationParams.isAllowExceedingBalance(true);
|
||||
|
||||
return transactionSimulator.process(
|
||||
param,
|
||||
transactionValidationParams.build(),
|
||||
TransactionValidationParams.transactionSimulatorAllowExceedingBalance(),
|
||||
OperationTracer.NO_TRACING,
|
||||
(mutableWorldState, transactionSimulatorResult) ->
|
||||
transactionSimulatorResult.map(
|
||||
|
||||
@@ -16,10 +16,12 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonCallParameterUtil.validateAndGetCallParams;
|
||||
|
||||
import org.hyperledger.besu.datatypes.AccountOverrideMap;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
|
||||
@@ -28,6 +30,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorR
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
@@ -35,9 +38,12 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
|
||||
import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
|
||||
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
|
||||
|
||||
private static final double SUB_CALL_REMAINING_GAS_RATIO = 65D / 64D;
|
||||
@@ -60,27 +66,24 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
|
||||
}
|
||||
}
|
||||
|
||||
protected Optional<BlockHeader> blockHeader(final long blockNumber) {
|
||||
if (getBlockchainQueries().headBlockNumber() == blockNumber) {
|
||||
// chain head header if cached, and we can return it form memory
|
||||
return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader());
|
||||
}
|
||||
return getBlockchainQueries().getBlockHeaderByNumber(blockNumber);
|
||||
}
|
||||
protected abstract Object simulate(
|
||||
final JsonRpcRequestContext requestContext,
|
||||
final CallParameter callParams,
|
||||
final long gasLimit,
|
||||
final TransactionSimulationFunction simulationFunction);
|
||||
|
||||
protected Optional<RpcErrorType> validateBlockHeader(
|
||||
final Optional<BlockHeader> maybeBlockHeader) {
|
||||
if (maybeBlockHeader.isEmpty()) {
|
||||
return Optional.of(RpcErrorType.BLOCK_NOT_FOUND);
|
||||
}
|
||||
|
||||
final var blockHeader = maybeBlockHeader.get();
|
||||
if (!getBlockchainQueries()
|
||||
.getWorldStateArchive()
|
||||
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
|
||||
return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE);
|
||||
}
|
||||
return Optional.empty();
|
||||
@Override
|
||||
protected Object pendingResult(final JsonRpcRequestContext requestContext) {
|
||||
final JsonCallParameter jsonCallParameter = validateAndGetCallParams(requestContext);
|
||||
final var validationParams = getTransactionValidationParams(jsonCallParameter);
|
||||
final var maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
|
||||
final var pendingBlockHeader = transactionSimulator.simulatePendingBlockHeader();
|
||||
final TransactionSimulationFunction simulationFunction =
|
||||
(cp, op) ->
|
||||
transactionSimulator.processOnPending(
|
||||
cp, maybeStateOverrides, validationParams, op, pendingBlockHeader);
|
||||
return simulate(
|
||||
requestContext, jsonCallParameter, pendingBlockHeader.getGasLimit(), simulationFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,13 +98,43 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
|
||||
return resultByBlockHeader(requestContext, jsonCallParameter, maybeBlockHeader.get());
|
||||
}
|
||||
|
||||
protected abstract Object resultByBlockHeader(
|
||||
private Object resultByBlockHeader(
|
||||
final JsonRpcRequestContext requestContext,
|
||||
final JsonCallParameter jsonCallParameter,
|
||||
final BlockHeader blockHeader);
|
||||
final BlockHeader blockHeader) {
|
||||
final var validationParams = getTransactionValidationParams(jsonCallParameter);
|
||||
final var maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
|
||||
final TransactionSimulationFunction simulationFunction =
|
||||
(cp, op) ->
|
||||
transactionSimulator.process(
|
||||
cp, maybeStateOverrides, validationParams, op, blockHeader);
|
||||
return simulate(
|
||||
requestContext, jsonCallParameter, blockHeader.getGasLimit(), simulationFunction);
|
||||
}
|
||||
|
||||
protected CallParameter overrideGasLimitAndPrice(
|
||||
final JsonCallParameter callParams, final long gasLimit) {
|
||||
private Optional<BlockHeader> blockHeader(final long blockNumber) {
|
||||
if (getBlockchainQueries().headBlockNumber() == blockNumber) {
|
||||
// chain head header if cached, and we can return it form memory
|
||||
return Optional.of(getBlockchainQueries().getBlockchain().getChainHeadHeader());
|
||||
}
|
||||
return getBlockchainQueries().getBlockHeaderByNumber(blockNumber);
|
||||
}
|
||||
|
||||
private Optional<RpcErrorType> validateBlockHeader(final Optional<BlockHeader> maybeBlockHeader) {
|
||||
if (maybeBlockHeader.isEmpty()) {
|
||||
return Optional.of(RpcErrorType.BLOCK_NOT_FOUND);
|
||||
}
|
||||
|
||||
final var blockHeader = maybeBlockHeader.get();
|
||||
if (!getBlockchainQueries()
|
||||
.getWorldStateArchive()
|
||||
.isWorldStateAvailable(blockHeader.getStateRoot(), blockHeader.getHash())) {
|
||||
return Optional.of(RpcErrorType.WORLD_STATE_UNAVAILABLE);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
protected CallParameter overrideGasLimit(final CallParameter callParams, final long gasLimit) {
|
||||
return new CallParameter(
|
||||
callParams.getChainId(),
|
||||
callParams.getFrom(),
|
||||
@@ -142,7 +175,7 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
|
||||
final ValidationResult<TransactionInvalidReason> validationResult =
|
||||
result.getValidationResult();
|
||||
if (validationResult != null && !validationResult.isValid()) {
|
||||
if (validationResult.getErrorMessage().length() > 0) {
|
||||
if (!validationResult.getErrorMessage().isEmpty()) {
|
||||
return errorResponse(request, JsonRpcError.from(validationResult));
|
||||
}
|
||||
return errorResponse(
|
||||
@@ -170,4 +203,29 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
|
||||
final JsonRpcRequestContext request, final JsonRpcError jsonRpcError) {
|
||||
return new JsonRpcErrorResponse(request.getRequest().getId(), jsonRpcError);
|
||||
}
|
||||
|
||||
protected static TransactionValidationParams getTransactionValidationParams(
|
||||
final JsonCallParameter callParams) {
|
||||
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
|
||||
|
||||
return isAllowExceedingBalance
|
||||
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
|
||||
: TransactionValidationParams.transactionSimulator();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
|
||||
final JsonRpcRequestContext request) {
|
||||
try {
|
||||
return request.getOptionalParameter(2, AccountOverrideMap.class);
|
||||
} catch (JsonRpcParameter.JsonRpcParameterException e) {
|
||||
throw new InvalidJsonRpcRequestException(
|
||||
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected interface TransactionSimulationFunction {
|
||||
Optional<TransactionSimulatorResult> simulate(
|
||||
CallParameter callParams, OperationTracer operationTracer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm.VmT
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.debug.TraceOptions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
@@ -117,9 +116,7 @@ public abstract class AbstractTraceByBlock extends AbstractBlockParameterMethod
|
||||
}
|
||||
|
||||
protected TransactionValidationParams buildTransactionValidationParams() {
|
||||
return ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.build();
|
||||
return TransactionValidationParams.transactionSimulator();
|
||||
}
|
||||
|
||||
protected TraceOptions buildTraceOptions(final Set<TraceTypeParameter.TraceType> traceTypes) {
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSucces
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
@@ -169,20 +168,18 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
|
||||
private TransactionValidationParams buildTransactionValidationParams(
|
||||
final BlockHeader header, final JsonCallParameter callParams) {
|
||||
|
||||
ImmutableTransactionValidationParams.Builder transactionValidationParams =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator());
|
||||
|
||||
final boolean isAllowExceedingBalance;
|
||||
// if it is not set explicitly whether we want a strict check of the balance or not. this will
|
||||
// be decided according to the provided parameters
|
||||
if (callParams.isMaybeStrict().isEmpty()) {
|
||||
transactionValidationParams.isAllowExceedingBalance(
|
||||
isAllowExceedingBalanceAutoSelection(header, callParams));
|
||||
isAllowExceedingBalance = isAllowExceedingBalanceAutoSelection(header, callParams);
|
||||
|
||||
} else {
|
||||
transactionValidationParams.isAllowExceedingBalance(
|
||||
!callParams.isMaybeStrict().orElse(Boolean.FALSE));
|
||||
isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
|
||||
}
|
||||
return transactionValidationParams.build();
|
||||
return isAllowExceedingBalance
|
||||
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
|
||||
: TransactionValidationParams.transactionSimulator();
|
||||
}
|
||||
|
||||
private boolean isAllowExceedingBalanceAutoSelection(
|
||||
|
||||
@@ -18,13 +18,9 @@ import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
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.parameters.JsonCallParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.CreateAccessListResult;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
|
||||
@@ -48,20 +44,24 @@ public class EthCreateAccessList extends AbstractEstimateGas {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resultByBlockHeader(
|
||||
protected Object simulate(
|
||||
final JsonRpcRequestContext requestContext,
|
||||
final JsonCallParameter jsonCallParameter,
|
||||
final BlockHeader blockHeader) {
|
||||
final AccessListSimulatorResult maybeResult =
|
||||
processTransaction(jsonCallParameter, blockHeader);
|
||||
final CallParameter callParams,
|
||||
final long gasLimit,
|
||||
final TransactionSimulationFunction simulationFunction) {
|
||||
|
||||
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
|
||||
final Optional<TransactionSimulatorResult> firstResult =
|
||||
simulationFunction.simulate(overrideGasLimit(callParams, gasLimit), tracer);
|
||||
|
||||
// if the call accessList is different from the simulation result, calculate gas and return
|
||||
if (shouldProcessWithAccessListOverride(jsonCallParameter, maybeResult.tracer())) {
|
||||
if (shouldProcessWithAccessListOverride(callParams, tracer)) {
|
||||
final AccessListSimulatorResult result =
|
||||
processTransactionWithAccessListOverride(
|
||||
jsonCallParameter, blockHeader, maybeResult.tracer().getAccessList());
|
||||
callParams, gasLimit, tracer.getAccessList(), simulationFunction);
|
||||
return createResponse(requestContext, result);
|
||||
} else {
|
||||
return createResponse(requestContext, maybeResult);
|
||||
return createResponse(requestContext, new AccessListSimulatorResult(firstResult, tracer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,16 +73,8 @@ public class EthCreateAccessList extends AbstractEstimateGas {
|
||||
.orElseGet(() -> errorResponse(requestContext, RpcErrorType.INTERNAL_ERROR));
|
||||
}
|
||||
|
||||
private TransactionValidationParams transactionValidationParams(
|
||||
final boolean isAllowExceedingBalance) {
|
||||
return ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(isAllowExceedingBalance)
|
||||
.build();
|
||||
}
|
||||
|
||||
private boolean shouldProcessWithAccessListOverride(
|
||||
final JsonCallParameter parameters, final AccessListOperationTracer tracer) {
|
||||
final CallParameter parameters, final AccessListOperationTracer tracer) {
|
||||
|
||||
// if empty, transaction did not access any storage, does not need to reprocess
|
||||
if (tracer.getAccessList().isEmpty()) {
|
||||
@@ -107,39 +99,23 @@ public class EthCreateAccessList extends AbstractEstimateGas {
|
||||
: errorResponse(request, result);
|
||||
}
|
||||
|
||||
private AccessListSimulatorResult processTransaction(
|
||||
final JsonCallParameter jsonCallParameter, final BlockHeader blockHeader) {
|
||||
final TransactionValidationParams transactionValidationParams =
|
||||
transactionValidationParams(!jsonCallParameter.isMaybeStrict().orElse(Boolean.FALSE));
|
||||
|
||||
final CallParameter callParams =
|
||||
overrideGasLimitAndPrice(jsonCallParameter, blockHeader.getGasLimit());
|
||||
|
||||
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
|
||||
final Optional<TransactionSimulatorResult> result =
|
||||
transactionSimulator.process(callParams, transactionValidationParams, tracer, blockHeader);
|
||||
return new AccessListSimulatorResult(result, tracer);
|
||||
}
|
||||
|
||||
private AccessListSimulatorResult processTransactionWithAccessListOverride(
|
||||
final JsonCallParameter jsonCallParameter,
|
||||
final BlockHeader blockHeader,
|
||||
final List<AccessListEntry> accessList) {
|
||||
final TransactionValidationParams transactionValidationParams =
|
||||
transactionValidationParams(!jsonCallParameter.isMaybeStrict().orElse(Boolean.FALSE));
|
||||
final CallParameter callParameter,
|
||||
final long gasLimit,
|
||||
final List<AccessListEntry> accessList,
|
||||
final TransactionSimulationFunction simulationFunction) {
|
||||
|
||||
final AccessListOperationTracer tracer = AccessListOperationTracer.create();
|
||||
final CallParameter callParameter =
|
||||
overrideAccessList(jsonCallParameter, blockHeader.getGasLimit(), accessList);
|
||||
final CallParameter modifiedCallParameter =
|
||||
overrideAccessList(callParameter, gasLimit, accessList);
|
||||
|
||||
final Optional<TransactionSimulatorResult> result =
|
||||
transactionSimulator.process(
|
||||
callParameter, transactionValidationParams, tracer, blockHeader);
|
||||
simulationFunction.simulate(modifiedCallParameter, tracer);
|
||||
return new AccessListSimulatorResult(result, tracer);
|
||||
}
|
||||
|
||||
protected CallParameter overrideAccessList(
|
||||
final JsonCallParameter callParams,
|
||||
private CallParameter overrideAccessList(
|
||||
final CallParameter callParams,
|
||||
final long gasLimit,
|
||||
final List<AccessListEntry> accessListEntries) {
|
||||
return new CallParameter(
|
||||
@@ -151,7 +127,7 @@ public class EthCreateAccessList extends AbstractEstimateGas {
|
||||
callParams.getMaxFeePerGas(),
|
||||
callParams.getValue(),
|
||||
callParams.getPayload(),
|
||||
Optional.ofNullable(accessListEntries));
|
||||
Optional.of(accessListEntries));
|
||||
}
|
||||
|
||||
private record AccessListSimulatorResult(
|
||||
|
||||
@@ -14,19 +14,12 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
|
||||
|
||||
import org.hyperledger.besu.datatypes.AccountOverrideMap;
|
||||
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.exception.InvalidJsonRpcRequestException;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
|
||||
@@ -34,7 +27,6 @@ import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -52,33 +44,17 @@ public class EthEstimateGas extends AbstractEstimateGas {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resultByBlockHeader(
|
||||
protected Object simulate(
|
||||
final JsonRpcRequestContext requestContext,
|
||||
final JsonCallParameter callParams,
|
||||
final BlockHeader blockHeader) {
|
||||
|
||||
final CallParameter modifiedCallParams =
|
||||
overrideGasLimitAndPrice(callParams, blockHeader.getGasLimit());
|
||||
Optional<AccountOverrideMap> maybeStateOverrides = getAddressAccountOverrideMap(requestContext);
|
||||
// TODO implement for block overrides
|
||||
|
||||
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
|
||||
final CallParameter callParams,
|
||||
final long gasLimit,
|
||||
final TransactionSimulationFunction simulationFunction) {
|
||||
|
||||
final EstimateGasOperationTracer operationTracer = new EstimateGasOperationTracer();
|
||||
final var transactionValidationParams =
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(isAllowExceedingBalance)
|
||||
.build();
|
||||
|
||||
LOG.debug("Processing transaction with params: {}", modifiedCallParams);
|
||||
LOG.debug("Processing transaction with params: {}", callParams);
|
||||
final var maybeResult =
|
||||
transactionSimulator.process(
|
||||
modifiedCallParams,
|
||||
maybeStateOverrides,
|
||||
transactionValidationParams,
|
||||
operationTracer,
|
||||
blockHeader);
|
||||
simulationFunction.simulate(overrideGasLimit(callParams, gasLimit), operationTracer);
|
||||
|
||||
final Optional<JsonRpcErrorResponse> maybeErrorResponse =
|
||||
validateSimulationResult(requestContext, maybeResult);
|
||||
@@ -89,12 +65,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
|
||||
final var result = maybeResult.get();
|
||||
long low = result.result().getEstimateGasUsedByTransaction();
|
||||
final var lowResult =
|
||||
transactionSimulator.process(
|
||||
overrideGasLimitAndPrice(callParams, low),
|
||||
maybeStateOverrides,
|
||||
transactionValidationParams,
|
||||
operationTracer,
|
||||
blockHeader);
|
||||
simulationFunction.simulate(overrideGasLimit(callParams, low), operationTracer);
|
||||
|
||||
if (lowResult.isPresent() && lowResult.get().isSuccessful()) {
|
||||
return Quantity.create(low);
|
||||
@@ -106,12 +77,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
|
||||
while (low + 1 < high) {
|
||||
mid = (low + high) / 2;
|
||||
var binarySearchResult =
|
||||
transactionSimulator.process(
|
||||
overrideGasLimitAndPrice(callParams, mid),
|
||||
maybeStateOverrides,
|
||||
transactionValidationParams,
|
||||
operationTracer,
|
||||
blockHeader);
|
||||
simulationFunction.simulate(overrideGasLimit(callParams, mid), operationTracer);
|
||||
|
||||
if (binarySearchResult.isEmpty() || !binarySearchResult.get().isSuccessful()) {
|
||||
low = mid;
|
||||
@@ -139,15 +105,4 @@ public class EthEstimateGas extends AbstractEstimateGas {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected Optional<AccountOverrideMap> getAddressAccountOverrideMap(
|
||||
final JsonRpcRequestContext request) {
|
||||
try {
|
||||
return request.getOptionalParameter(2, AccountOverrideMap.class);
|
||||
} catch (JsonRpcParameter.JsonRpcParameterException e) {
|
||||
throw new InvalidJsonRpcRequestException(
|
||||
"Invalid account overrides parameter (index 2)", RpcErrorType.INVALID_CALL_PARAMS, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +116,7 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
|
||||
.getAndMapWorldState(
|
||||
blockHeader.getBlockHash(),
|
||||
ws -> {
|
||||
final WorldUpdater updater =
|
||||
transactionSimulator.getEffectiveWorldStateUpdater(blockHeader, ws);
|
||||
final WorldUpdater updater = transactionSimulator.getEffectiveWorldStateUpdater(ws);
|
||||
try {
|
||||
Arrays.stream(transactionsAndTraceTypeParameters)
|
||||
.forEachOrdered(
|
||||
@@ -158,6 +157,11 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
|
||||
final Set<TraceTypeParameter.TraceType> traceTypes = traceTypeParameter.getTraceTypes();
|
||||
final DebugOperationTracer tracer =
|
||||
new DebugOperationTracer(buildTraceOptions(traceTypes), false);
|
||||
final var miningBeneficiary =
|
||||
protocolSchedule
|
||||
.getByBlockHeader(header)
|
||||
.getMiningBeneficiaryCalculator()
|
||||
.calculateBeneficiary(header);
|
||||
final Optional<TransactionSimulatorResult> maybeSimulatorResult =
|
||||
transactionSimulator.processWithWorldUpdater(
|
||||
callParameter,
|
||||
@@ -165,7 +169,8 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
|
||||
buildTransactionValidationParams(),
|
||||
tracer,
|
||||
header,
|
||||
worldUpdater);
|
||||
worldUpdater,
|
||||
miningBeneficiary);
|
||||
|
||||
LOG.trace("Executing {} call for transaction {}", traceTypeParameter, callParameter);
|
||||
if (maybeSimulatorResult.isEmpty()) {
|
||||
|
||||
@@ -389,8 +389,7 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
|
||||
|
||||
// fcU calls are synchronous, no need to make volatile
|
||||
private long lastFcuInfoLog = System.currentTimeMillis();
|
||||
private static final String logMessage =
|
||||
"{} for fork-choice-update: head: {}, finalized: {}, safeBlockHash: {}";
|
||||
private static final String logMessage = "FCU({}) | head: {} | finalized: {} | safeBlockHash: {}";
|
||||
|
||||
private void logForkchoiceUpdatedCall(
|
||||
final EngineStatus status, final EngineForkchoiceUpdatedParameter forkChoice) {
|
||||
@@ -413,9 +412,9 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJso
|
||||
LOG.info(
|
||||
logMessage,
|
||||
status.name(),
|
||||
forkChoice.getHeadBlockHash(),
|
||||
forkChoice.getFinalizedBlockHash(),
|
||||
forkChoice.getSafeBlockHash());
|
||||
forkChoice.getHeadBlockHash().toShortLogString(),
|
||||
forkChoice.getFinalizedBlockHash().toShortLogString(),
|
||||
forkChoice.getSafeBlockHash().toShortLogString());
|
||||
}
|
||||
|
||||
private void logAtDebug(
|
||||
|
||||
@@ -370,7 +370,8 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
.flatMap(Optional::stream)
|
||||
.mapToInt(List::size)
|
||||
.sum(),
|
||||
(System.currentTimeMillis() - startTimeMs) / 1000.0);
|
||||
(System.currentTimeMillis() - startTimeMs) / 1000.0,
|
||||
executionResult.getNbParallelizedTransations());
|
||||
return respondWith(reqId, blockParam, newBlockHeader.getHash(), VALID);
|
||||
} else {
|
||||
if (executionResult.causedBy().isPresent()) {
|
||||
@@ -564,26 +565,41 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private void logImportedBlockInfo(final Block block, final int blobCount, final double timeInS) {
|
||||
private void logImportedBlockInfo(
|
||||
final Block block,
|
||||
final int blobCount,
|
||||
final double timeInS,
|
||||
final Optional<Integer> nbParallelizedTransations) {
|
||||
final StringBuilder message = new StringBuilder();
|
||||
message.append("Imported #%,d / %d tx");
|
||||
final int nbTransactions = block.getBody().getTransactions().size();
|
||||
message.append("Imported #%,d (%s)|%5d tx");
|
||||
final List<Object> messageArgs =
|
||||
new ArrayList<>(
|
||||
List.of(block.getHeader().getNumber(), block.getBody().getTransactions().size()));
|
||||
List.of(
|
||||
block.getHeader().getNumber(), block.getHash().toShortLogString(), nbTransactions));
|
||||
if (block.getBody().getWithdrawals().isPresent()) {
|
||||
message.append(" / %d ws");
|
||||
message.append("|%3d ws");
|
||||
messageArgs.add(block.getBody().getWithdrawals().get().size());
|
||||
}
|
||||
message.append(" / %d blobs / base fee %s / %,d (%01.1f%%) gas / (%s) in %01.3fs. Peers: %d");
|
||||
double mgasPerSec = (timeInS != 0) ? block.getHeader().getGasUsed() / (timeInS * 1_000_000) : 0;
|
||||
message.append(
|
||||
"|%2d blobs| base fee %s| gas used %,11d (%5.1f%%)| exec time %01.3fs| mgas/s %6.2f");
|
||||
messageArgs.addAll(
|
||||
List.of(
|
||||
blobCount,
|
||||
block.getHeader().getBaseFee().map(Wei::toHumanReadableString).orElse("N/A"),
|
||||
block.getHeader().getBaseFee().map(Wei::toHumanReadablePaddedString).orElse("N/A"),
|
||||
block.getHeader().getGasUsed(),
|
||||
(block.getHeader().getGasUsed() * 100.0) / block.getHeader().getGasLimit(),
|
||||
block.getHash().toHexString(),
|
||||
timeInS,
|
||||
ethPeers.peerCount()));
|
||||
mgasPerSec));
|
||||
if (nbParallelizedTransations.isPresent()) {
|
||||
double parallelizedTxPercentage =
|
||||
(double) (nbParallelizedTransations.get() * 100) / nbTransactions;
|
||||
message.append("| parallel txs %5.1f%%");
|
||||
messageArgs.add(parallelizedTxPercentage);
|
||||
}
|
||||
message.append("| peers: %2d");
|
||||
messageArgs.add(ethPeers.peerCount());
|
||||
LOG.info(String.format(message.toString(), messageArgs.toArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.JwtAlgorithm;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -54,11 +58,13 @@ public class WebSocketConfiguration {
|
||||
private Optional<String> keyStorePath = Optional.empty();
|
||||
private Optional<String> keyStorePassword = Optional.empty();
|
||||
private Optional<String> keyStoreType = Optional.of("JKS"); // Default to JKS
|
||||
private Optional<String> keyStorePasswordFile = Optional.empty();
|
||||
|
||||
private boolean clientAuthEnabled = false;
|
||||
private Optional<String> trustStorePath = Optional.empty();
|
||||
private Optional<String> trustStorePassword = Optional.empty();
|
||||
private Optional<String> trustStoreType = Optional.of("JKS"); // Default to JKS
|
||||
private Optional<String> trustStorePasswordFile = Optional.empty();
|
||||
|
||||
// For PEM format
|
||||
private Optional<String> keyPath = Optional.empty();
|
||||
@@ -191,8 +197,11 @@ public class WebSocketConfiguration {
|
||||
this.keyStorePath = Optional.ofNullable(keyStorePath);
|
||||
}
|
||||
|
||||
public Optional<String> getKeyStorePassword() {
|
||||
return keyStorePassword;
|
||||
public Optional<String> getKeyStorePassword() throws IOException {
|
||||
if (keyStorePassword.isPresent()) {
|
||||
return keyStorePassword;
|
||||
}
|
||||
return Optional.ofNullable(getKeystorePasswordFromFile());
|
||||
}
|
||||
|
||||
public void setKeyStorePassword(final String keyStorePassword) {
|
||||
@@ -245,8 +254,11 @@ public class WebSocketConfiguration {
|
||||
}
|
||||
|
||||
// Truststore Password
|
||||
public Optional<String> getTrustStorePassword() {
|
||||
return trustStorePassword;
|
||||
public Optional<String> getTrustStorePassword() throws IOException {
|
||||
if (trustStorePassword.isPresent()) {
|
||||
return trustStorePassword;
|
||||
}
|
||||
return Optional.ofNullable(getTruststorePasswordFromFile());
|
||||
}
|
||||
|
||||
public void setTrustStorePassword(final String trustStorePassword) {
|
||||
@@ -258,6 +270,38 @@ public class WebSocketConfiguration {
|
||||
return trustStoreType;
|
||||
}
|
||||
|
||||
public void setKeyStorePasswordFile(final String keyStorePasswordFile) {
|
||||
this.keyStorePasswordFile = Optional.ofNullable(keyStorePasswordFile);
|
||||
}
|
||||
|
||||
public void setTrustStorePasswordFile(final String trustStorePasswordFile) {
|
||||
this.trustStorePasswordFile = Optional.ofNullable(trustStorePasswordFile);
|
||||
}
|
||||
|
||||
private String loadPasswordFromFile(final String passwordFile) throws IOException {
|
||||
if (passwordFile != null) {
|
||||
Path path = Path.of(passwordFile);
|
||||
if (Files.exists(path)) {
|
||||
return Files.readString(path, StandardCharsets.UTF_8).trim();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getKeystorePasswordFromFile() throws IOException {
|
||||
if (keyStorePasswordFile.isPresent()) {
|
||||
return loadPasswordFromFile(keyStorePasswordFile.get());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getTruststorePasswordFromFile() throws IOException {
|
||||
if (trustStorePasswordFile.isPresent()) {
|
||||
return loadPasswordFromFile(trustStorePasswordFile.get());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setTrustStoreType(final String trustStoreType) {
|
||||
this.trustStoreType = Optional.ofNullable(trustStoreType);
|
||||
}
|
||||
|
||||
@@ -122,9 +122,16 @@ public class WebSocketService {
|
||||
// Check if SSL/TLS is enabled in the configuration
|
||||
if (configuration.isSslEnabled()) {
|
||||
serverOptions.setSsl(true);
|
||||
String keystorePassword = null;
|
||||
|
||||
String keystorePath = configuration.getKeyStorePath().orElse(null);
|
||||
String keystorePassword = configuration.getKeyStorePassword().orElse(null);
|
||||
try {
|
||||
keystorePassword = configuration.getKeyStorePassword().orElse(null);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error reading keystore password", e);
|
||||
resultFuture.completeExceptionally(e);
|
||||
return resultFuture;
|
||||
}
|
||||
String keyPath = configuration.getKeyPath().orElse(null);
|
||||
String certPath = configuration.getCertPath().orElse(null);
|
||||
|
||||
@@ -146,9 +153,16 @@ public class WebSocketService {
|
||||
// Set up truststore for client authentication (mTLS)
|
||||
if (configuration.isClientAuthEnabled()) {
|
||||
serverOptions.setClientAuth(ClientAuth.REQUIRED);
|
||||
String truststorePassword;
|
||||
|
||||
String truststorePath = configuration.getTrustStorePath().orElse(null);
|
||||
String truststorePassword = configuration.getTrustStorePassword().orElse("");
|
||||
try {
|
||||
truststorePassword = configuration.getTrustStorePassword().orElse(null);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error reading truststore password", e);
|
||||
resultFuture.completeExceptionally(e);
|
||||
return resultFuture;
|
||||
}
|
||||
String truststoreType = configuration.getTrustStoreType().orElse("JKS");
|
||||
String trustCertPath = configuration.getTrustCertPath().orElse(null);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.chain.BadBlockManager;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.DefaultSyncStatus;
|
||||
import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -141,6 +142,7 @@ public abstract class AbstractEthGraphQLHttpServiceTest {
|
||||
blockchain,
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
blockchainSetupUtil.getProtocolSchedule(),
|
||||
ImmutableMiningConfiguration.newDefault(),
|
||||
0L);
|
||||
|
||||
service =
|
||||
|
||||
@@ -175,6 +175,7 @@ public abstract class AbstractJsonRpcHttpServiceTest {
|
||||
blockchainSetupUtil.getBlockchain(),
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
blockchainSetupUtil.getProtocolSchedule(),
|
||||
miningConfiguration,
|
||||
0L);
|
||||
|
||||
return new JsonRpcMethodsFactory()
|
||||
|
||||
@@ -72,6 +72,7 @@ public class EthCreateAccessListTest {
|
||||
@Mock private BlockHeader latestBlockHeader;
|
||||
@Mock private BlockHeader finalizedBlockHeader;
|
||||
@Mock private BlockHeader genesisBlockHeader;
|
||||
@Mock private BlockHeader pendingBlockHeader;
|
||||
@Mock private Blockchain blockchain;
|
||||
@Mock private BlockchainQueries blockchainQueries;
|
||||
@Mock private TransactionSimulator transactionSimulator;
|
||||
@@ -93,6 +94,9 @@ public class EthCreateAccessListTest {
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader);
|
||||
when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
|
||||
when(latestBlockHeader.getNumber()).thenReturn(2L);
|
||||
when(pendingBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
|
||||
when(pendingBlockHeader.getNumber()).thenReturn(3L);
|
||||
when(transactionSimulator.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader);
|
||||
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true);
|
||||
|
||||
method = new EthCreateAccessList(blockchainQueries, transactionSimulator);
|
||||
@@ -139,6 +143,18 @@ public class EthCreateAccessListTest {
|
||||
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingBlockTagEstimateOnPendingBlock() {
|
||||
final JsonRpcRequestContext request =
|
||||
ethCreateAccessListRequest(legacyTransactionCallParameter(Wei.ZERO), "pending");
|
||||
mockTransactionSimulatorResult(true, false, 1L, pendingBlockHeader);
|
||||
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(null, new CreateAccessListResult(new ArrayList<>(), 1L));
|
||||
|
||||
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnGasEstimateErrorWhenGasPricePresentForEip1559Transaction() {
|
||||
final JsonRpcRequestContext request =
|
||||
@@ -186,7 +202,8 @@ public class EthCreateAccessListTest {
|
||||
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
|
||||
|
||||
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
|
||||
verify(transactionSimulator, times(1))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -207,7 +224,8 @@ public class EthCreateAccessListTest {
|
||||
assertThat(responseWithMockTracer(request, tracer))
|
||||
.usingRecursiveComparison()
|
||||
.isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader));
|
||||
verify(transactionSimulator, times(2))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -224,7 +242,8 @@ public class EthCreateAccessListTest {
|
||||
// Set TransactionSimulator.process response
|
||||
mockTransactionSimulatorResult(true, false, 1L, latestBlockHeader);
|
||||
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
|
||||
verify(transactionSimulator, times(1))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,7 +264,8 @@ public class EthCreateAccessListTest {
|
||||
assertThat(responseWithMockTracer(request, tracer))
|
||||
.usingRecursiveComparison()
|
||||
.isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(1)).process(any(), any(), any(), eq(latestBlockHeader));
|
||||
verify(transactionSimulator, times(1))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -269,7 +289,8 @@ public class EthCreateAccessListTest {
|
||||
assertThat(responseWithMockTracer(request, tracer))
|
||||
.usingRecursiveComparison()
|
||||
.isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(latestBlockHeader));
|
||||
verify(transactionSimulator, times(2))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(latestBlockHeader));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -289,7 +310,8 @@ public class EthCreateAccessListTest {
|
||||
assertThat(responseWithMockTracer(request, tracer))
|
||||
.usingRecursiveComparison()
|
||||
.isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(finalizedBlockHeader));
|
||||
verify(transactionSimulator, times(2))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(finalizedBlockHeader));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -309,7 +331,8 @@ public class EthCreateAccessListTest {
|
||||
assertThat(responseWithMockTracer(request, tracer))
|
||||
.usingRecursiveComparison()
|
||||
.isEqualTo(expectedResponse);
|
||||
verify(transactionSimulator, times(2)).process(any(), any(), any(), eq(genesisBlockHeader));
|
||||
verify(transactionSimulator, times(2))
|
||||
.process(any(), eq(Optional.empty()), any(), any(), eq(genesisBlockHeader));
|
||||
}
|
||||
|
||||
private JsonRpcResponse responseWithMockTracer(
|
||||
@@ -328,14 +351,21 @@ public class EthCreateAccessListTest {
|
||||
return tracer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
private void mockTransactionSimulatorResult(
|
||||
final boolean isSuccessful,
|
||||
final boolean isReverted,
|
||||
final long estimateGas,
|
||||
final BlockHeader blockHeader) {
|
||||
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
|
||||
when(transactionSimulator.process(any(), any(), any(), eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
if (blockHeader == pendingBlockHeader) {
|
||||
when(transactionSimulator.processOnPending(
|
||||
any(), eq(Optional.empty()), any(), any(), eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
} else {
|
||||
when(transactionSimulator.process(any(), eq(Optional.empty()), any(), any(), eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
}
|
||||
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
|
||||
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);
|
||||
when(mockResult.getRevertReason())
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
|
||||
@@ -70,6 +69,7 @@ public class EthEstimateGasTest {
|
||||
@Mock private BlockHeader latestBlockHeader;
|
||||
@Mock private BlockHeader finalizedBlockHeader;
|
||||
@Mock private BlockHeader genesisBlockHeader;
|
||||
@Mock private BlockHeader pendingBlockHeader;
|
||||
@Mock private Blockchain blockchain;
|
||||
@Mock private BlockchainQueries blockchainQueries;
|
||||
@Mock private TransactionSimulator transactionSimulator;
|
||||
@@ -91,6 +91,9 @@ public class EthEstimateGasTest {
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(latestBlockHeader);
|
||||
when(latestBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
|
||||
when(latestBlockHeader.getNumber()).thenReturn(2L);
|
||||
when(pendingBlockHeader.getGasLimit()).thenReturn(Long.MAX_VALUE);
|
||||
when(pendingBlockHeader.getNumber()).thenReturn(3L);
|
||||
when(transactionSimulator.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader);
|
||||
when(worldStateArchive.isWorldStateAvailable(any(), any())).thenReturn(true);
|
||||
|
||||
method = new EthEstimateGas(blockchainQueries, transactionSimulator);
|
||||
@@ -376,11 +379,7 @@ public class EthEstimateGasTest {
|
||||
.process(
|
||||
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
eq(
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(true)
|
||||
.build()),
|
||||
eq(TransactionValidationParams.transactionSimulatorAllowExceedingBalance()),
|
||||
any(OperationTracer.class),
|
||||
eq(latestBlockHeader));
|
||||
}
|
||||
@@ -397,11 +396,7 @@ public class EthEstimateGasTest {
|
||||
.process(
|
||||
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
eq(
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(false)
|
||||
.build()),
|
||||
eq(TransactionValidationParams.transactionSimulator()),
|
||||
any(OperationTracer.class),
|
||||
eq(latestBlockHeader));
|
||||
}
|
||||
@@ -432,6 +427,17 @@ public class EthEstimateGasTest {
|
||||
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingBlockTagEstimateOnPendingBlock() {
|
||||
final JsonRpcRequestContext request =
|
||||
ethEstimateGasRequest(eip1559TransactionCallParameter(), "pending");
|
||||
mockTransientProcessorResultGasEstimate(1L, true, false, pendingBlockHeader);
|
||||
|
||||
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
|
||||
|
||||
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUseBlockNumberParamWhenPresent() {
|
||||
final JsonRpcRequestContext request =
|
||||
@@ -488,6 +494,7 @@ public class EthEstimateGasTest {
|
||||
isSuccessful, estimateGas, gasPrice, revertReason, blockHeader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
private TransactionSimulatorResult getMockTransactionSimulatorResult(
|
||||
final boolean isSuccessful,
|
||||
final long estimateGas,
|
||||
@@ -495,21 +502,37 @@ public class EthEstimateGasTest {
|
||||
final Optional<Bytes> revertReason,
|
||||
final BlockHeader blockHeader) {
|
||||
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
|
||||
when(transactionSimulator.process(
|
||||
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
any(TransactionValidationParams.class),
|
||||
any(OperationTracer.class),
|
||||
eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
when(transactionSimulator.process(
|
||||
eq(modifiedEip1559TransactionCallParameter()),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
any(TransactionValidationParams.class),
|
||||
any(OperationTracer.class),
|
||||
eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
|
||||
if (blockHeader == pendingBlockHeader) {
|
||||
when(transactionSimulator.processOnPending(
|
||||
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
any(TransactionValidationParams.class),
|
||||
any(OperationTracer.class),
|
||||
eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
when(transactionSimulator.processOnPending(
|
||||
eq(modifiedEip1559TransactionCallParameter()),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
any(TransactionValidationParams.class),
|
||||
any(OperationTracer.class),
|
||||
eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
} else {
|
||||
when(transactionSimulator.process(
|
||||
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
any(TransactionValidationParams.class),
|
||||
any(OperationTracer.class),
|
||||
eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
when(transactionSimulator.process(
|
||||
eq(modifiedEip1559TransactionCallParameter()),
|
||||
eq(Optional.empty()), // no account overrides
|
||||
any(TransactionValidationParams.class),
|
||||
any(OperationTracer.class),
|
||||
eq(blockHeader)))
|
||||
.thenReturn(Optional.of(mockTxSimResult));
|
||||
}
|
||||
final TransactionProcessingResult mockResult = mock(TransactionProcessingResult.class);
|
||||
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);
|
||||
when(mockResult.getRevertReason()).thenReturn(revertReason);
|
||||
|
||||
@@ -16,10 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -124,7 +121,6 @@ public class TransactionTracerTest {
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
|
||||
when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator);
|
||||
when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager);
|
||||
lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -30,6 +30,9 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
@@ -390,14 +393,20 @@ public class WebSocketServiceTLSTest {
|
||||
clientTrustStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
File tempFile = File.createTempFile("pwdfile", ".txt");
|
||||
tempFile.deleteOnExit();
|
||||
try (Writer writer = Files.newBufferedWriter(tempFile.toPath(), Charset.defaultCharset())) {
|
||||
writer.write("password");
|
||||
}
|
||||
|
||||
// Configure WebSocket with SSL and client authentication enabled
|
||||
config.setSslEnabled(true);
|
||||
config.setKeyStorePath(serverKeystoreFile.getAbsolutePath());
|
||||
config.setKeyStorePassword("password");
|
||||
config.setKeyStorePasswordFile(tempFile.getAbsolutePath());
|
||||
config.setKeyStoreType("PKCS12");
|
||||
config.setClientAuthEnabled(true);
|
||||
config.setTrustStorePath(serverTruststoreFile.getAbsolutePath());
|
||||
config.setTrustStorePassword("password");
|
||||
config.setTrustStorePasswordFile(tempFile.getAbsolutePath());
|
||||
config.setTrustStoreType("PKCS12");
|
||||
|
||||
// Create and start WebSocketService
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.blockcreation;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.core.BlockHeaderBuilder.createPending;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
@@ -29,7 +30,6 @@ import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
|
||||
@@ -41,15 +41,12 @@ import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BodyValidation;
|
||||
import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
|
||||
import org.hyperledger.besu.ethereum.mainnet.requests.ProcessRequestContext;
|
||||
import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator;
|
||||
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
|
||||
@@ -60,7 +57,6 @@ import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleExcepti
|
||||
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
|
||||
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CancellationException;
|
||||
@@ -198,12 +194,15 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
protocolSchedule.getForNextBlockHeader(parentHeader, timestamp);
|
||||
|
||||
final ProcessableBlockHeader processableBlockHeader =
|
||||
createPendingBlockHeader(
|
||||
timestamp,
|
||||
maybePrevRandao,
|
||||
maybeParentBeaconBlockRoot,
|
||||
newProtocolSpec,
|
||||
parentHeader);
|
||||
createPending(
|
||||
newProtocolSpec,
|
||||
parentHeader,
|
||||
miningConfiguration,
|
||||
timestamp,
|
||||
maybePrevRandao,
|
||||
maybeParentBeaconBlockRoot)
|
||||
.buildProcessableBlockHeader();
|
||||
|
||||
final Address miningBeneficiary =
|
||||
miningBeneficiaryCalculator.getMiningBeneficiary(processableBlockHeader.getNumber());
|
||||
|
||||
@@ -421,52 +420,6 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
private ProcessableBlockHeader createPendingBlockHeader(
|
||||
final long timestamp,
|
||||
final Optional<Bytes32> maybePrevRandao,
|
||||
final Optional<Bytes32> maybeParentBeaconBlockRoot,
|
||||
final ProtocolSpec protocolSpec,
|
||||
final BlockHeader parentHeader) {
|
||||
final long newBlockNumber = parentHeader.getNumber() + 1;
|
||||
long gasLimit =
|
||||
protocolSpec
|
||||
.getGasLimitCalculator()
|
||||
.nextGasLimit(
|
||||
parentHeader.getGasLimit(),
|
||||
miningConfiguration.getTargetGasLimit().orElse(parentHeader.getGasLimit()),
|
||||
newBlockNumber);
|
||||
|
||||
final DifficultyCalculator difficultyCalculator = protocolSpec.getDifficultyCalculator();
|
||||
final BigInteger difficulty = difficultyCalculator.nextDifficulty(timestamp, parentHeader);
|
||||
|
||||
final Wei baseFee =
|
||||
Optional.of(protocolSpec.getFeeMarket())
|
||||
.filter(FeeMarket::implementsBaseFee)
|
||||
.map(BaseFeeMarket.class::cast)
|
||||
.map(
|
||||
feeMarket ->
|
||||
feeMarket.computeBaseFee(
|
||||
newBlockNumber,
|
||||
parentHeader.getBaseFee().orElse(Wei.ZERO),
|
||||
parentHeader.getGasUsed(),
|
||||
feeMarket.targetGasUsed(parentHeader)))
|
||||
.orElse(null);
|
||||
|
||||
final Bytes32 prevRandao = maybePrevRandao.orElse(null);
|
||||
final Bytes32 parentBeaconBlockRoot = maybeParentBeaconBlockRoot.orElse(null);
|
||||
return BlockHeaderBuilder.create()
|
||||
.parentHash(parentHeader.getHash())
|
||||
.coinbase(miningConfiguration.getCoinbase().orElseThrow())
|
||||
.difficulty(Difficulty.of(difficulty))
|
||||
.number(newBlockNumber)
|
||||
.gasLimit(gasLimit)
|
||||
.timestamp(timestamp)
|
||||
.baseFee(baseFee)
|
||||
.prevRandao(prevRandao)
|
||||
.parentBeaconBlockRoot(parentBeaconBlockRoot)
|
||||
.buildProcessableBlockHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
isCancelled.set(true);
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -61,7 +61,7 @@ class BlobSizeTransactionSelectorTest {
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
private static final KeyPair KEYS = SIGNATURE_ALGORITHM.get().generateKeyPair();
|
||||
|
||||
private static final long BLOB_GAS_PER_BLOB = CancunGasCalculator.BLOB_GAS_PER_BLOB;
|
||||
private static final long BLOB_GAS_PER_BLOB = new CancunGasCalculator().getBlobGasPerBlob();
|
||||
private static final int MAX_BLOBS = 6;
|
||||
private static final long MAX_BLOB_GAS = BLOB_GAS_PER_BLOB * MAX_BLOBS;
|
||||
|
||||
@@ -89,8 +89,8 @@ class BlobSizeTransactionSelectorTest {
|
||||
@Test
|
||||
void firstBlobTransactionIsSelected() {
|
||||
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
|
||||
when(blockSelectionContext.gasCalculator().blobGasCost(anyInt()))
|
||||
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class));
|
||||
when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
|
||||
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
|
||||
|
||||
final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
|
||||
|
||||
@@ -131,8 +131,8 @@ class BlobSizeTransactionSelectorTest {
|
||||
@Test
|
||||
void returnsTooLargeForRemainingBlobGas() {
|
||||
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
|
||||
when(blockSelectionContext.gasCalculator().blobGasCost(anyInt()))
|
||||
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class));
|
||||
when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
|
||||
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
|
||||
|
||||
final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public class BlockProcessingResult extends BlockValidationResult {
|
||||
|
||||
private final Optional<BlockProcessingOutputs> yield;
|
||||
private final boolean isPartial;
|
||||
private Optional<Integer> nbParallelizedTransations = Optional.empty();
|
||||
|
||||
/** A result indicating that processing failed. */
|
||||
public static final BlockProcessingResult FAILED = new BlockProcessingResult("processing failed");
|
||||
@@ -40,6 +41,21 @@ public class BlockProcessingResult extends BlockValidationResult {
|
||||
this.isPartial = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A result indicating that processing was successful but incomplete.
|
||||
*
|
||||
* @param yield the outputs of processing a block
|
||||
* @param nbParallelizedTransations potential number of parallelized transactions during block
|
||||
* processing
|
||||
*/
|
||||
public BlockProcessingResult(
|
||||
final Optional<BlockProcessingOutputs> yield,
|
||||
final Optional<Integer> nbParallelizedTransations) {
|
||||
this.yield = yield;
|
||||
this.isPartial = false;
|
||||
this.nbParallelizedTransations = nbParallelizedTransations;
|
||||
}
|
||||
|
||||
/**
|
||||
* A result indicating that processing was successful but incomplete.
|
||||
*
|
||||
@@ -144,4 +160,13 @@ public class BlockProcessingResult extends BlockValidationResult {
|
||||
public Optional<List<Request>> getRequests() {
|
||||
return yield.flatMap(BlockProcessingOutputs::getRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional that contains the number of parallelized transactions (if there is any)
|
||||
*
|
||||
* @return Optional of parallelized transactions during the block execution
|
||||
*/
|
||||
public Optional<Integer> getNbParallelizedTransations() {
|
||||
return nbParallelizedTransations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,8 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
}
|
||||
|
||||
return new BlockProcessingResult(
|
||||
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)));
|
||||
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)),
|
||||
result.getNbParallelizedTransations());
|
||||
}
|
||||
} catch (MerkleTrieException ex) {
|
||||
context.getWorldStateArchive().heal(ex.getMaybeAddress(), ex.getLocation());
|
||||
|
||||
@@ -22,10 +22,14 @@ import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.BlobGas;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.mainnet.DifficultyCalculator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
|
||||
import org.hyperledger.besu.evm.log.LogsBloomFilter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -158,6 +162,55 @@ public class BlockHeaderBuilder {
|
||||
return toBuilder;
|
||||
}
|
||||
|
||||
public static BlockHeaderBuilder createPending(
|
||||
final ProtocolSpec protocolSpec,
|
||||
final BlockHeader parentHeader,
|
||||
final MiningConfiguration miningConfiguration,
|
||||
final long timestamp,
|
||||
final Optional<Bytes32> maybePrevRandao,
|
||||
final Optional<Bytes32> maybeParentBeaconBlockRoot) {
|
||||
|
||||
final long newBlockNumber = parentHeader.getNumber() + 1;
|
||||
final long gasLimit =
|
||||
protocolSpec
|
||||
.getGasLimitCalculator()
|
||||
.nextGasLimit(
|
||||
parentHeader.getGasLimit(),
|
||||
miningConfiguration.getTargetGasLimit().orElse(parentHeader.getGasLimit()),
|
||||
newBlockNumber);
|
||||
|
||||
final DifficultyCalculator difficultyCalculator = protocolSpec.getDifficultyCalculator();
|
||||
final var difficulty =
|
||||
Difficulty.of(difficultyCalculator.nextDifficulty(timestamp, parentHeader));
|
||||
|
||||
final Wei baseFee;
|
||||
if (protocolSpec.getFeeMarket().implementsBaseFee()) {
|
||||
final var baseFeeMarket = (BaseFeeMarket) protocolSpec.getFeeMarket();
|
||||
baseFee =
|
||||
baseFeeMarket.computeBaseFee(
|
||||
newBlockNumber,
|
||||
parentHeader.getBaseFee().orElse(Wei.ZERO),
|
||||
parentHeader.getGasUsed(),
|
||||
baseFeeMarket.targetGasUsed(parentHeader));
|
||||
} else {
|
||||
baseFee = null;
|
||||
}
|
||||
|
||||
final Bytes32 prevRandao = maybePrevRandao.orElse(null);
|
||||
final Bytes32 parentBeaconBlockRoot = maybeParentBeaconBlockRoot.orElse(null);
|
||||
|
||||
return BlockHeaderBuilder.create()
|
||||
.parentHash(parentHeader.getHash())
|
||||
.coinbase(miningConfiguration.getCoinbase().orElseThrow())
|
||||
.difficulty(difficulty)
|
||||
.number(newBlockNumber)
|
||||
.gasLimit(gasLimit)
|
||||
.timestamp(timestamp)
|
||||
.baseFee(baseFee)
|
||||
.prevRandao(prevRandao)
|
||||
.parentBeaconBlockRoot(parentBeaconBlockRoot);
|
||||
}
|
||||
|
||||
public BlockHeader buildBlockHeader() {
|
||||
validateBlockHeader();
|
||||
|
||||
|
||||
@@ -196,4 +196,25 @@ public class ProcessableBlockHeader
|
||||
public String toLogString() {
|
||||
return getNumber() + " (time: " + getTimestamp() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("ProcessableBlockHeader{");
|
||||
sb.append("number=").append(number).append(", ");
|
||||
sb.append("parentHash=").append(parentHash).append(", ");
|
||||
sb.append("coinbase=").append(coinbase).append(", ");
|
||||
sb.append("difficulty=").append(difficulty).append(", ");
|
||||
sb.append("gasLimit=").append(gasLimit).append(", ");
|
||||
sb.append("timestamp=").append(timestamp).append(", ");
|
||||
sb.append("baseFee=").append(baseFee).append(", ");
|
||||
sb.append("mixHashOrPrevRandao=").append(mixHashOrPrevRandao).append(", ");
|
||||
if (parentBeaconBlockRoot != null) {
|
||||
sb.append("parentBeaconBlockRoot=").append(parentBeaconBlockRoot).append(", ");
|
||||
}
|
||||
if (targetBlobsPerBlock != null) {
|
||||
sb.append("targetBlobsPerBlock=").append(targetBlobsPerBlock);
|
||||
}
|
||||
return sb.append("}").toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState;
|
||||
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
|
||||
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
import org.hyperledger.besu.evm.operation.BlockHashOperation;
|
||||
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldState;
|
||||
@@ -136,6 +135,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
blockHashLookup,
|
||||
blobGasPrice);
|
||||
|
||||
boolean parallelizedTxFound = false;
|
||||
int nbParallelTx = 0;
|
||||
for (int i = 0; i < transactions.size(); i++) {
|
||||
final Transaction transaction = transactions.get(i);
|
||||
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
|
||||
@@ -174,13 +175,21 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining();
|
||||
if (transaction.getVersionedHashes().isPresent()) {
|
||||
currentBlobGasUsed +=
|
||||
(transaction.getVersionedHashes().get().size() * CancunGasCalculator.BLOB_GAS_PER_BLOB);
|
||||
(transaction.getVersionedHashes().get().size()
|
||||
* protocolSpec.getGasCalculator().getBlobGasPerBlob());
|
||||
}
|
||||
|
||||
final TransactionReceipt transactionReceipt =
|
||||
transactionReceiptFactory.create(
|
||||
transaction.getType(), transactionProcessingResult, worldState, currentGasUsed);
|
||||
receipts.add(transactionReceipt);
|
||||
if (!parallelizedTxFound
|
||||
&& transactionProcessingResult.getIsProcessedInParallel().isPresent()) {
|
||||
parallelizedTxFound = true;
|
||||
nbParallelTx = 1;
|
||||
} else if (transactionProcessingResult.getIsProcessedInParallel().isPresent()) {
|
||||
nbParallelTx++;
|
||||
}
|
||||
}
|
||||
if (blockHeader.getBlobGasUsed().isPresent()
|
||||
&& currentBlobGasUsed != blockHeader.getBlobGasUsed().get()) {
|
||||
@@ -243,7 +252,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
|
||||
}
|
||||
|
||||
return new BlockProcessingResult(
|
||||
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)));
|
||||
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)),
|
||||
parallelizedTxFound ? Optional.of(nbParallelTx) : Optional.empty());
|
||||
}
|
||||
|
||||
protected Optional<PreprocessingContext> runBlockPreProcessing(
|
||||
|
||||
@@ -35,6 +35,9 @@ public interface TransactionValidationParams {
|
||||
TransactionValidationParams transactionSimulatorParams =
|
||||
ImmutableTransactionValidationParams.of(false, false, false, false, false, true);
|
||||
|
||||
TransactionValidationParams transactionSimulatorAllowExceedingBalanceParams =
|
||||
ImmutableTransactionValidationParams.of(false, true, false, false, false, true);
|
||||
|
||||
@Value.Default
|
||||
default boolean isAllowFutureNonce() {
|
||||
return false;
|
||||
@@ -69,6 +72,10 @@ public interface TransactionValidationParams {
|
||||
return transactionSimulatorParams;
|
||||
}
|
||||
|
||||
static TransactionValidationParams transactionSimulatorAllowExceedingBalance() {
|
||||
return transactionSimulatorAllowExceedingBalanceParams;
|
||||
}
|
||||
|
||||
static TransactionValidationParams processingBlock() {
|
||||
return processingBlockParams;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ public class ExcessBlobGasCalculator {
|
||||
.getGasCalculator()
|
||||
.computeExcessBlobGas(
|
||||
parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L),
|
||||
parentHeader.getBlobGasUsed().orElse(0L));
|
||||
parentHeader.getBlobGasUsed().orElse(0L),
|
||||
parentHeader.getTargetBlobsPerBlock());
|
||||
return BlobGas.of(headerExcess);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.mainnet.headervalidationrules;
|
||||
import org.hyperledger.besu.datatypes.BlobGas;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -45,7 +44,8 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule
|
||||
long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L);
|
||||
|
||||
long calculatedExcessBlobGas =
|
||||
gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed);
|
||||
gasCalculator.computeExcessBlobGas(
|
||||
parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobsPerBlock());
|
||||
|
||||
if (headerExcessBlobGas != calculatedExcessBlobGas) {
|
||||
LOG.info(
|
||||
@@ -55,10 +55,9 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule
|
||||
return false;
|
||||
}
|
||||
long headerBlobGasUsed = header.getBlobGasUsed().orElse(0L);
|
||||
if (headerBlobGasUsed % CancunGasCalculator.BLOB_GAS_PER_BLOB != 0) {
|
||||
if (headerBlobGasUsed % gasCalculator.getBlobGasPerBlob() != 0) {
|
||||
LOG.info(
|
||||
"blob gas used must be multiple of GAS_PER_BLOB ({})",
|
||||
CancunGasCalculator.BLOB_GAS_PER_BLOB);
|
||||
"blob gas used must be multiple of GAS_PER_BLOB ({})", gasCalculator.getBlobGasPerBlob());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -251,8 +251,10 @@ public class ParallelizedConcurrentTransactionProcessor {
|
||||
|
||||
blockAccumulator.importStateChangesFromSource(transactionAccumulator);
|
||||
|
||||
if (confirmedParallelizedTransactionCounter.isPresent())
|
||||
if (confirmedParallelizedTransactionCounter.isPresent()) {
|
||||
confirmedParallelizedTransactionCounter.get().inc();
|
||||
transactionProcessingResult.setIsProcessedInParallel(Optional.of(Boolean.TRUE));
|
||||
}
|
||||
return Optional.of(transactionProcessingResult);
|
||||
} else {
|
||||
blockAccumulator.importPriorStateFromSource(transactionAccumulator);
|
||||
|
||||
@@ -49,6 +49,8 @@ public class TransactionProcessingResult
|
||||
|
||||
private final Bytes output;
|
||||
|
||||
private Optional<Boolean> isProcessedInParallel = Optional.empty();
|
||||
|
||||
private final ValidationResult<TransactionInvalidReason> validationResult;
|
||||
private final Optional<Bytes> revertReason;
|
||||
|
||||
@@ -194,6 +196,25 @@ public class TransactionProcessingResult
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set isProcessedInParallel to the value in parameter
|
||||
*
|
||||
* @param isProcessedInParallel new value of isProcessedInParallel
|
||||
*/
|
||||
public void setIsProcessedInParallel(final Optional<Boolean> isProcessedInParallel) {
|
||||
this.isProcessedInParallel = isProcessedInParallel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag that indicates if the transaction was executed in parallel
|
||||
*
|
||||
* @return Optional of Boolean, the value of the boolean is true if the transaction was executed
|
||||
* in parallel
|
||||
*/
|
||||
public Optional<Boolean> getIsProcessedInParallel() {
|
||||
return isProcessedInParallel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reason why a transaction was reverted (if applicable).
|
||||
*
|
||||
|
||||
@@ -28,9 +28,10 @@ import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
@@ -84,16 +85,19 @@ public class TransactionSimulator {
|
||||
private final Blockchain blockchain;
|
||||
private final WorldStateArchive worldStateArchive;
|
||||
private final ProtocolSchedule protocolSchedule;
|
||||
private final MiningConfiguration miningConfiguration;
|
||||
private final long rpcGasCap;
|
||||
|
||||
public TransactionSimulator(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final ProtocolSchedule protocolSchedule,
|
||||
final MiningConfiguration miningConfiguration,
|
||||
final long rpcGasCap) {
|
||||
this.blockchain = blockchain;
|
||||
this.worldStateArchive = worldStateArchive;
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
this.miningConfiguration = miningConfiguration;
|
||||
this.rpcGasCap = rpcGasCap;
|
||||
}
|
||||
|
||||
@@ -141,14 +145,82 @@ public class TransactionSimulator {
|
||||
blockHeader);
|
||||
}
|
||||
|
||||
public Optional<TransactionSimulatorResult> processOnPending(
|
||||
final CallParameter callParams,
|
||||
final Optional<AccountOverrideMap> maybeStateOverrides,
|
||||
final TransactionValidationParams transactionValidationParams,
|
||||
final OperationTracer operationTracer,
|
||||
final ProcessableBlockHeader pendingBlockHeader) {
|
||||
|
||||
try (final MutableWorldState disposableWorldState =
|
||||
duplicateWorldStateAtParent(pendingBlockHeader.getParentHash())) {
|
||||
WorldUpdater updater = getEffectiveWorldStateUpdater(disposableWorldState);
|
||||
|
||||
// in order to trace the state diff we need to make sure that
|
||||
// the world updater always has a parent
|
||||
if (operationTracer instanceof DebugOperationTracer) {
|
||||
updater = updater.parentUpdater().isPresent() ? updater : updater.updater();
|
||||
}
|
||||
|
||||
return processWithWorldUpdater(
|
||||
callParams,
|
||||
maybeStateOverrides,
|
||||
transactionValidationParams,
|
||||
operationTracer,
|
||||
pendingBlockHeader,
|
||||
updater,
|
||||
Address.ZERO);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ProcessableBlockHeader simulatePendingBlockHeader() {
|
||||
final long timestamp = System.currentTimeMillis();
|
||||
final var chainHeadHeader = blockchain.getChainHeadHeader();
|
||||
final ProtocolSpec protocolSpec =
|
||||
protocolSchedule.getForNextBlockHeader(chainHeadHeader, timestamp);
|
||||
|
||||
final var simulatedBlockHeader =
|
||||
BlockHeaderBuilder.createPending(
|
||||
protocolSpec,
|
||||
chainHeadHeader,
|
||||
miningConfiguration,
|
||||
timestamp,
|
||||
Optional.empty(),
|
||||
Optional.empty())
|
||||
.buildProcessableBlockHeader();
|
||||
|
||||
LOG.trace("Simulated block header: {}", simulatedBlockHeader);
|
||||
|
||||
return simulatedBlockHeader;
|
||||
}
|
||||
|
||||
private MutableWorldState duplicateWorldStateAtParent(final Hash parentHash) {
|
||||
final var parentHeader =
|
||||
blockchain
|
||||
.getBlockHeader(parentHash)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException("Block with hash " + parentHash + " not available"));
|
||||
|
||||
final Hash parentStateRoot = parentHeader.getStateRoot();
|
||||
return worldStateArchive
|
||||
.getMutable(parentHeader, false)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
"World state not available for block "
|
||||
+ parentHeader.getNumber()
|
||||
+ " with state root "
|
||||
+ parentStateRoot));
|
||||
}
|
||||
|
||||
public Optional<TransactionSimulatorResult> processAtHead(final CallParameter callParams) {
|
||||
final var chainHeadHash = blockchain.getChainHeadHash();
|
||||
return process(
|
||||
callParams,
|
||||
ImmutableTransactionValidationParams.builder()
|
||||
.from(TransactionValidationParams.transactionSimulator())
|
||||
.isAllowExceedingBalance(true)
|
||||
.build(),
|
||||
TransactionValidationParams.transactionSimulatorAllowExceedingBalance(),
|
||||
OperationTracer.NO_TRACING,
|
||||
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,
|
||||
blockchain
|
||||
@@ -209,7 +281,7 @@ public class TransactionSimulator {
|
||||
|
||||
try (final MutableWorldState ws = getWorldState(header)) {
|
||||
|
||||
WorldUpdater updater = getEffectiveWorldStateUpdater(header, ws);
|
||||
WorldUpdater updater = getEffectiveWorldStateUpdater(ws);
|
||||
|
||||
// in order to trace the state diff we need to make sure that
|
||||
// the world updater always has a parent
|
||||
@@ -217,6 +289,12 @@ public class TransactionSimulator {
|
||||
updater = updater.parentUpdater().isPresent() ? updater : updater.updater();
|
||||
}
|
||||
|
||||
final var miningBeneficiary =
|
||||
protocolSchedule
|
||||
.getByBlockHeader(header)
|
||||
.getMiningBeneficiaryCalculator()
|
||||
.calculateBeneficiary(header);
|
||||
|
||||
return preWorldStateCloseGuard.apply(
|
||||
ws,
|
||||
processWithWorldUpdater(
|
||||
@@ -225,7 +303,8 @@ public class TransactionSimulator {
|
||||
transactionValidationParams,
|
||||
operationTracer,
|
||||
header,
|
||||
updater));
|
||||
updater,
|
||||
miningBeneficiary));
|
||||
|
||||
} catch (final Exception e) {
|
||||
return Optional.empty();
|
||||
@@ -267,21 +346,25 @@ public class TransactionSimulator {
|
||||
final Optional<AccountOverrideMap> maybeStateOverrides,
|
||||
final TransactionValidationParams transactionValidationParams,
|
||||
final OperationTracer operationTracer,
|
||||
final BlockHeader header,
|
||||
final WorldUpdater updater) {
|
||||
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(header);
|
||||
final ProcessableBlockHeader processableHeader,
|
||||
final WorldUpdater updater,
|
||||
final Address miningBeneficiary) {
|
||||
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(processableHeader);
|
||||
|
||||
final Address senderAddress =
|
||||
callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM;
|
||||
|
||||
BlockHeader blockHeaderToProcess = header;
|
||||
|
||||
if (transactionValidationParams.isAllowExceedingBalance() && header.getBaseFee().isPresent()) {
|
||||
final ProcessableBlockHeader blockHeaderToProcess;
|
||||
if (transactionValidationParams.isAllowExceedingBalance()
|
||||
&& processableHeader.getBaseFee().isPresent()) {
|
||||
blockHeaderToProcess =
|
||||
BlockHeaderBuilder.fromHeader(header)
|
||||
new BlockHeaderBuilder()
|
||||
.populateFrom(processableHeader)
|
||||
.baseFee(Wei.ZERO)
|
||||
.blockHeaderFunctions(protocolSpec.getBlockHeaderFunctions())
|
||||
.buildBlockHeader();
|
||||
.buildProcessableBlockHeader();
|
||||
} else {
|
||||
blockHeaderToProcess = processableHeader;
|
||||
}
|
||||
if (maybeStateOverrides.isPresent()) {
|
||||
for (Address accountToOverride : maybeStateOverrides.get().keySet()) {
|
||||
@@ -296,9 +379,6 @@ public class TransactionSimulator {
|
||||
final long simulationGasCap =
|
||||
calculateSimulationGasCap(callParams.getGasLimit(), blockHeaderToProcess.getGasLimit());
|
||||
|
||||
final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO;
|
||||
final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY;
|
||||
|
||||
final MainnetTransactionProcessor transactionProcessor =
|
||||
protocolSchedule.getByBlockHeader(blockHeaderToProcess).getTransactionProcessor();
|
||||
|
||||
@@ -318,12 +398,10 @@ public class TransactionSimulator {
|
||||
buildTransaction(
|
||||
callParams,
|
||||
transactionValidationParams,
|
||||
header,
|
||||
processableHeader,
|
||||
senderAddress,
|
||||
nonce,
|
||||
simulationGasCap,
|
||||
value,
|
||||
payload,
|
||||
blobGasPrice);
|
||||
if (maybeTransaction.isEmpty()) {
|
||||
return Optional.empty();
|
||||
@@ -335,9 +413,7 @@ public class TransactionSimulator {
|
||||
updater,
|
||||
blockHeaderToProcess,
|
||||
transaction,
|
||||
protocolSpec
|
||||
.getMiningBeneficiaryCalculator()
|
||||
.calculateBeneficiary(blockHeaderToProcess),
|
||||
miningBeneficiary,
|
||||
new CachingBlockHashLookup(blockHeaderToProcess, blockchain),
|
||||
false,
|
||||
transactionValidationParams,
|
||||
@@ -400,13 +476,15 @@ public class TransactionSimulator {
|
||||
private Optional<Transaction> buildTransaction(
|
||||
final CallParameter callParams,
|
||||
final TransactionValidationParams transactionValidationParams,
|
||||
final BlockHeader header,
|
||||
final ProcessableBlockHeader processableHeader,
|
||||
final Address senderAddress,
|
||||
final long nonce,
|
||||
final long gasLimit,
|
||||
final Wei value,
|
||||
final Bytes payload,
|
||||
final Wei blobGasPrice) {
|
||||
|
||||
final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO;
|
||||
final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY;
|
||||
|
||||
final Transaction.Builder transactionBuilder =
|
||||
Transaction.builder()
|
||||
.nonce(nonce)
|
||||
@@ -437,18 +515,21 @@ public class TransactionSimulator {
|
||||
maxPriorityFeePerGas = callParams.getMaxPriorityFeePerGas().orElse(gasPrice);
|
||||
maxFeePerBlobGas = callParams.getMaxFeePerBlobGas().orElse(blobGasPrice);
|
||||
}
|
||||
if (header.getBaseFee().isEmpty()) {
|
||||
|
||||
if (shouldSetGasPrice(callParams, processableHeader)) {
|
||||
transactionBuilder.gasPrice(gasPrice);
|
||||
} else if (protocolSchedule.getChainId().isPresent()) {
|
||||
}
|
||||
|
||||
if (shouldSetMaxFeePerGas(callParams, processableHeader)) {
|
||||
transactionBuilder.maxFeePerGas(maxFeePerGas).maxPriorityFeePerGas(maxPriorityFeePerGas);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (shouldSetBlobGasPrice(callParams)) {
|
||||
transactionBuilder.maxFeePerBlobGas(maxFeePerBlobGas);
|
||||
}
|
||||
|
||||
transactionBuilder.guessType();
|
||||
if (transactionBuilder.getTransactionType().supportsBlob()) {
|
||||
transactionBuilder.maxFeePerBlobGas(maxFeePerBlobGas);
|
||||
}
|
||||
|
||||
if (transactionBuilder.getTransactionType().requiresChainId()) {
|
||||
callParams
|
||||
.getChainId()
|
||||
@@ -463,8 +544,7 @@ public class TransactionSimulator {
|
||||
return Optional.ofNullable(transaction);
|
||||
}
|
||||
|
||||
public WorldUpdater getEffectiveWorldStateUpdater(
|
||||
final BlockHeader header, final MutableWorldState publicWorldState) {
|
||||
public WorldUpdater getEffectiveWorldStateUpdater(final MutableWorldState publicWorldState) {
|
||||
return publicWorldState.updater();
|
||||
}
|
||||
|
||||
@@ -489,4 +569,41 @@ public class TransactionSimulator {
|
||||
|
||||
return Optional.of(worldState.get(address) != null);
|
||||
}
|
||||
|
||||
private boolean shouldSetGasPrice(
|
||||
final CallParameter callParams, final ProcessableBlockHeader header) {
|
||||
if (header.getBaseFee().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if maxPriorityFeePerGas and maxFeePerGas are not set, use gasPrice
|
||||
return callParams.getMaxPriorityFeePerGas().isEmpty() && callParams.getMaxFeePerGas().isEmpty();
|
||||
}
|
||||
|
||||
private boolean shouldSetMaxFeePerGas(
|
||||
final CallParameter callParams, final ProcessableBlockHeader header) {
|
||||
if (protocolSchedule.getChainId().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.getBaseFee().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shouldSetBlobGasPrice(callParams)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// only set maxFeePerGas and maxPriorityFeePerGas if they are present, otherwise transaction
|
||||
// will be considered EIP-1559 transaction even if the simulation is for a legacy transaction
|
||||
return callParams.getMaxPriorityFeePerGas().isPresent()
|
||||
|| callParams.getMaxFeePerGas().isPresent();
|
||||
}
|
||||
|
||||
private boolean shouldSetBlobGasPrice(final CallParameter callParams) {
|
||||
if (protocolSchedule.getChainId().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return callParams.getBlobVersionedHashes().isPresent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,6 +504,8 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
|
||||
tracked.setStorageWasCleared(false); // storage already cleared for this transaction
|
||||
}
|
||||
});
|
||||
getUpdatedAccounts().clear();
|
||||
getDeletedAccounts().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,7 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason
|
||||
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -589,7 +589,7 @@ public class MainnetTransactionValidatorTest {
|
||||
|
||||
@Test
|
||||
public void shouldAcceptTransactionWithAtLeastOneBlob() {
|
||||
when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L);
|
||||
when(gasCalculator.blobGasCost(anyLong())).thenReturn(2L);
|
||||
final TransactionValidator validator =
|
||||
createTransactionValidator(
|
||||
gasCalculator,
|
||||
|
||||
@@ -20,26 +20,37 @@ import org.hyperledger.besu.datatypes.BlobGas;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
|
||||
|
||||
import org.apache.tuweni.units.bigints.UInt64;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for the {@link BlobGasValidationRule} class. */
|
||||
public class BlobGasValidationRuleTest {
|
||||
|
||||
private CancunGasCalculator gasCalculator;
|
||||
private BlobGasValidationRule blobGasValidationRule;
|
||||
private CancunGasCalculator cancunGasCalculator;
|
||||
private BlobGasValidationRule cancunBlobGasValidationRule;
|
||||
|
||||
private PragueGasCalculator pragueGasCalculator;
|
||||
private BlobGasValidationRule pragueBlobGasValidationRule;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
gasCalculator = new CancunGasCalculator();
|
||||
blobGasValidationRule = new BlobGasValidationRule(gasCalculator);
|
||||
cancunGasCalculator = new CancunGasCalculator();
|
||||
cancunBlobGasValidationRule = new BlobGasValidationRule(cancunGasCalculator);
|
||||
|
||||
pragueGasCalculator = new PragueGasCalculator();
|
||||
pragueBlobGasValidationRule = new BlobGasValidationRule(pragueGasCalculator);
|
||||
}
|
||||
|
||||
/** Tests that the header blob gas matches the calculated blob gas and passes validation. */
|
||||
/**
|
||||
* Cancun EIP-4844 - Tests that the header blob gas matches the calculated blob gas and passes
|
||||
* validation.
|
||||
*/
|
||||
@Test
|
||||
public void validateHeader_BlobGasMatchesCalculated_SuccessValidation() {
|
||||
long target = gasCalculator.getTargetBlobGasPerBlock();
|
||||
long target = cancunGasCalculator.getTargetBlobGasPerBlock();
|
||||
|
||||
// Create parent header
|
||||
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
|
||||
@@ -52,15 +63,16 @@ public class BlobGasValidationRuleTest {
|
||||
headerBuilder.excessBlobGas(BlobGas.of(1L));
|
||||
final BlockHeader header = headerBuilder.buildHeader();
|
||||
|
||||
assertThat(blobGasValidationRule.validate(header, parentHeader)).isTrue();
|
||||
assertThat(cancunBlobGasValidationRule.validate(header, parentHeader)).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the header blob gas is different from the calculated blob gas and fails validation.
|
||||
* Cancun EIP-4844 - Tests that the header blob gas is different from the calculated blob gas and
|
||||
* fails validation.
|
||||
*/
|
||||
@Test
|
||||
public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() {
|
||||
long target = gasCalculator.getTargetBlobGasPerBlock();
|
||||
long target = cancunGasCalculator.getTargetBlobGasPerBlock();
|
||||
|
||||
// Create parent header
|
||||
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
|
||||
@@ -72,6 +84,48 @@ public class BlobGasValidationRuleTest {
|
||||
final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
|
||||
final BlockHeader header = headerBuilder.buildHeader();
|
||||
|
||||
assertThat(blobGasValidationRule.validate(header, parentHeader)).isFalse();
|
||||
assertThat(cancunBlobGasValidationRule.validate(header, parentHeader)).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes
|
||||
* validation.
|
||||
*/
|
||||
@Test
|
||||
public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target3() {
|
||||
// Create parent header
|
||||
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
|
||||
parentBuilder.excessBlobGas(BlobGas.of(1L));
|
||||
parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(3));
|
||||
parentBuilder.targetBlobsPerBlock(UInt64.valueOf(3));
|
||||
final BlockHeader parentHeader = parentBuilder.buildHeader();
|
||||
|
||||
// Create block header with matching excessBlobGas
|
||||
final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
|
||||
headerBuilder.excessBlobGas(BlobGas.of(1L));
|
||||
final BlockHeader header = headerBuilder.buildHeader();
|
||||
|
||||
assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes
|
||||
* validation.
|
||||
*/
|
||||
@Test
|
||||
public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target4() {
|
||||
// Create parent header
|
||||
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
|
||||
parentBuilder.excessBlobGas(BlobGas.of(1L));
|
||||
parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(4));
|
||||
parentBuilder.targetBlobsPerBlock(UInt64.valueOf(4));
|
||||
final BlockHeader parentHeader = parentBuilder.buildHeader();
|
||||
|
||||
// Create block header with matching excessBlobGas
|
||||
final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
|
||||
headerBuilder.excessBlobGas(BlobGas.of(1L));
|
||||
final BlockHeader header = headerBuilder.buildHeader();
|
||||
|
||||
assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
package org.hyperledger.besu.ethereum.transaction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.evm.tracing.OperationTracer.NO_TRACING;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -32,12 +34,14 @@ import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.GasLimitCalculator;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlobTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
|
||||
@@ -52,7 +56,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult.Stat
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.tracing.OperationTracer;
|
||||
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -99,10 +103,13 @@ public class TransactionSimulatorTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
final var miningConfiguration = MiningConfiguration.newDefault().setCoinbase(Address.ZERO);
|
||||
this.transactionSimulator =
|
||||
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, 0);
|
||||
new TransactionSimulator(
|
||||
blockchain, worldStateArchive, protocolSchedule, miningConfiguration, 0);
|
||||
this.cappedTransactionSimulator =
|
||||
new TransactionSimulator(blockchain, worldStateArchive, protocolSchedule, GAS_CAP);
|
||||
new TransactionSimulator(
|
||||
blockchain, worldStateArchive, protocolSchedule, miningConfiguration, GAS_CAP);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -182,6 +189,43 @@ public class TransactionSimulatorTest {
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simulateOnPendingBlockWorks() {
|
||||
final CallParameter callParameter = eip1559TransactionCallParameter();
|
||||
|
||||
final BlockHeader blockHeader = mockBlockHeader(Hash.ZERO, 1L, Wei.ONE);
|
||||
|
||||
mockBlockchainForBlockHeader(blockHeader);
|
||||
mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L);
|
||||
|
||||
final Transaction expectedTransaction =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.EIP1559)
|
||||
.chainId(BigInteger.ONE)
|
||||
.nonce(1L)
|
||||
.gasLimit(blockHeader.getGasLimit())
|
||||
.maxFeePerGas(callParameter.getMaxFeePerGas().orElseThrow())
|
||||
.maxPriorityFeePerGas(callParameter.getMaxPriorityFeePerGas().orElseThrow())
|
||||
.to(callParameter.getTo())
|
||||
.sender(callParameter.getFrom())
|
||||
.value(callParameter.getValue())
|
||||
.payload(callParameter.getPayload())
|
||||
.signature(FAKE_SIGNATURE)
|
||||
.build();
|
||||
mockProcessorStatusForTransaction(expectedTransaction, Status.SUCCESSFUL);
|
||||
|
||||
final Optional<TransactionSimulatorResult> result =
|
||||
transactionSimulator.processOnPending(
|
||||
callParameter,
|
||||
Optional.empty(),
|
||||
TransactionValidationParams.transactionSimulator(),
|
||||
NO_TRACING,
|
||||
transactionSimulator.simulatePendingBlockHeader());
|
||||
|
||||
assertThat(result.get().isSuccessful()).isTrue();
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSetGasPriceToZeroWhenExceedingBalanceAllowed() {
|
||||
final CallParameter callParameter = legacyTransactionCallParameter(Wei.ONE);
|
||||
@@ -209,7 +253,7 @@ public class TransactionSimulatorTest {
|
||||
transactionSimulator.process(
|
||||
callParameter,
|
||||
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(true).build(),
|
||||
OperationTracer.NO_TRACING,
|
||||
NO_TRACING,
|
||||
1L);
|
||||
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -245,7 +289,7 @@ public class TransactionSimulatorTest {
|
||||
transactionSimulator.process(
|
||||
callParameter,
|
||||
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(true).build(),
|
||||
OperationTracer.NO_TRACING,
|
||||
NO_TRACING,
|
||||
1L);
|
||||
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -279,7 +323,7 @@ public class TransactionSimulatorTest {
|
||||
transactionSimulator.process(
|
||||
callParameter,
|
||||
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(false).build(),
|
||||
OperationTracer.NO_TRACING,
|
||||
NO_TRACING,
|
||||
1L);
|
||||
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -314,7 +358,7 @@ public class TransactionSimulatorTest {
|
||||
transactionSimulator.process(
|
||||
callParameter,
|
||||
ImmutableTransactionValidationParams.builder().isAllowExceedingBalance(false).build(),
|
||||
OperationTracer.NO_TRACING,
|
||||
NO_TRACING,
|
||||
1L);
|
||||
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -600,10 +644,7 @@ public class TransactionSimulatorTest {
|
||||
|
||||
// call process with original transaction
|
||||
cappedTransactionSimulator.process(
|
||||
callParameter,
|
||||
TransactionValidationParams.transactionSimulator(),
|
||||
OperationTracer.NO_TRACING,
|
||||
1L);
|
||||
callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L);
|
||||
|
||||
// expect overwritten transaction to be processed
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -638,10 +679,7 @@ public class TransactionSimulatorTest {
|
||||
|
||||
// call process with original transaction
|
||||
cappedTransactionSimulator.process(
|
||||
callParameter,
|
||||
TransactionValidationParams.transactionSimulator(),
|
||||
OperationTracer.NO_TRACING,
|
||||
1L);
|
||||
callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L);
|
||||
|
||||
// expect overwritten transaction to be processed
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -677,10 +715,7 @@ public class TransactionSimulatorTest {
|
||||
|
||||
// call process with original transaction
|
||||
cappedTransactionSimulator.process(
|
||||
callParameter,
|
||||
TransactionValidationParams.transactionSimulator(),
|
||||
OperationTracer.NO_TRACING,
|
||||
1L);
|
||||
callParameter, TransactionValidationParams.transactionSimulator(), NO_TRACING, 1L);
|
||||
|
||||
// expect transaction with the original gas limit to be processed
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
@@ -799,6 +834,8 @@ public class TransactionSimulatorTest {
|
||||
when(blockchain.getBlockHeader(blockHeader.getNumber())).thenReturn(Optional.of(blockHeader));
|
||||
when(blockchain.getBlockHeader(blockHeader.getBlockHash()))
|
||||
.thenReturn(Optional.of(blockHeader));
|
||||
when(blockchain.getChainHeadHash()).thenReturn(blockHeader.getHash());
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
|
||||
}
|
||||
|
||||
private void mockProtocolSpecForProcessWithWorldUpdater() {
|
||||
@@ -806,11 +843,15 @@ public class TransactionSimulatorTest {
|
||||
final BlockHashProcessor blockHashProcessor = mock(BlockHashProcessor.class);
|
||||
when(protocolSchedule.getChainId()).thenReturn(Optional.of(BigInteger.ONE));
|
||||
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
|
||||
when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(protocolSpec);
|
||||
when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor);
|
||||
when(protocolSpec.getMiningBeneficiaryCalculator()).thenReturn(BlockHeader::getCoinbase);
|
||||
when(protocolSpec.getBlockHeaderFunctions()).thenReturn(blockHeaderFunctions);
|
||||
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0));
|
||||
when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor);
|
||||
when(protocolSpec.getGasCalculator()).thenReturn(new FrontierGasCalculator());
|
||||
when(protocolSpec.getGasLimitCalculator()).thenReturn(GasLimitCalculator.constant());
|
||||
when(protocolSpec.getDifficultyCalculator()).thenReturn((time, parent) -> BigInteger.TEN);
|
||||
}
|
||||
|
||||
private void mockProcessorStatusForTransaction(
|
||||
@@ -912,4 +953,43 @@ public class TransactionSimulatorTest {
|
||||
Optional.of(maxFeePerBlobGas),
|
||||
Optional.of(bwc.getVersionedHashes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSimulateLegacyTransactionWhenBaseFeeNotZero() {
|
||||
// tests that the transaction simulator will simulate a legacy transaction when the base fee is
|
||||
// not zero
|
||||
// and the transaction is a legacy transaction
|
||||
|
||||
final CallParameter callParameter = legacyTransactionCallParameter();
|
||||
|
||||
final BlockHeader blockHeader =
|
||||
blockHeaderTestFixture
|
||||
.number(1L)
|
||||
.stateRoot(Hash.ZERO)
|
||||
.baseFeePerGas(Wei.of(7))
|
||||
.buildHeader();
|
||||
|
||||
mockBlockchainForBlockHeader(blockHeader);
|
||||
mockWorldStateForAccount(blockHeader, callParameter.getFrom(), 1L);
|
||||
|
||||
final Transaction expectedTransaction =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.FRONTIER)
|
||||
.nonce(1L)
|
||||
.gasPrice(callParameter.getGasPrice())
|
||||
.gasLimit(blockHeader.getGasLimit())
|
||||
.to(callParameter.getTo())
|
||||
.sender(callParameter.getFrom())
|
||||
.value(callParameter.getValue())
|
||||
.payload(callParameter.getPayload())
|
||||
.signature(FAKE_SIGNATURE)
|
||||
.build();
|
||||
mockProcessorStatusForTransaction(expectedTransaction, Status.SUCCESSFUL);
|
||||
|
||||
final Optional<TransactionSimulatorResult> result =
|
||||
transactionSimulator.process(callParameter, 1L);
|
||||
|
||||
verifyTransactionWasProcessed(expectedTransaction);
|
||||
assertThat(result.get().isSuccessful()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.hyperledger.besu.config.JsonUtil;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.chain.GenesisState;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
|
||||
@@ -70,7 +71,8 @@ public class NodeSmartContractPermissioningControllerTest {
|
||||
genesisState.writeStateTo(worldArchive.getMutable());
|
||||
|
||||
final TransactionSimulator ts =
|
||||
new TransactionSimulator(blockchain, worldArchive, protocolSchedule, 0L);
|
||||
new TransactionSimulator(
|
||||
blockchain, worldArchive, protocolSchedule, MiningConfiguration.newDefault(), 0L);
|
||||
final Address contractAddress = Address.fromHexString(contractAddressString);
|
||||
|
||||
when(metricsSystem.createCounter(
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.chain.GenesisState;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
@@ -69,7 +70,8 @@ public class TransactionSmartContractPermissioningControllerTest {
|
||||
genesisState.writeStateTo(worldArchive.getMutable());
|
||||
|
||||
final TransactionSimulator ts =
|
||||
new TransactionSimulator(blockchain, worldArchive, protocolSchedule, 0L);
|
||||
new TransactionSimulator(
|
||||
blockchain, worldArchive, protocolSchedule, MiningConfiguration.newDefault(), 0L);
|
||||
final Address contractAddress = Address.fromHexString(contractAddressString);
|
||||
|
||||
when(metricsSystem.createCounter(
|
||||
|
||||
@@ -44,13 +44,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
|
||||
private static final long TSTORE_GAS = WARM_STORAGE_READ_COST;
|
||||
|
||||
/**
|
||||
* The blob gas cost per blob. This is the gas cost for each blob of blob that is added to the
|
||||
* The blob gas cost per blob. This is the gas cost for each blob of data that is added to the
|
||||
* block.
|
||||
*/
|
||||
public static final long BLOB_GAS_PER_BLOB = 1 << 17;
|
||||
private static final long BLOB_GAS_PER_BLOB = 1 << 17;
|
||||
|
||||
/** The target blob gas per block. */
|
||||
public static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000;
|
||||
static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000;
|
||||
|
||||
// EIP-1153
|
||||
@Override
|
||||
@@ -64,8 +64,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long blobGasCost(final int blobCount) {
|
||||
return BLOB_GAS_PER_BLOB * blobCount;
|
||||
public long blobGasCost(final long blobCount) {
|
||||
return getBlobGasPerBlob() * blobCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBlobGasPerBlob() {
|
||||
return BLOB_GAS_PER_BLOB;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,35 +81,4 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
|
||||
public long getTargetBlobGasPerBlock() {
|
||||
return TARGET_BLOB_GAS_PER_BLOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the excess blob gas for a given block based on the parent's excess blob gas and blob
|
||||
* gas used. If the sum of parent's excess blob gas and parent's blob gas used is less than the
|
||||
* target blob gas per block, the excess blob gas is calculated as 0. Otherwise, it is computed as
|
||||
* the difference between the sum and the target blob gas per block.
|
||||
*
|
||||
* @param parentExcessBlobGas The excess blob gas of the parent block.
|
||||
* @param newBlobs blob gas incurred by current block
|
||||
* @return The excess blob gas for the current block.
|
||||
*/
|
||||
@Override
|
||||
public long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) {
|
||||
final long consumedBlobGas = blobGasCost(newBlobs);
|
||||
final long currentExcessBlobGas = parentExcessBlobGas + consumedBlobGas;
|
||||
|
||||
if (currentExcessBlobGas < TARGET_BLOB_GAS_PER_BLOCK) {
|
||||
return 0L;
|
||||
}
|
||||
return currentExcessBlobGas - TARGET_BLOB_GAS_PER_BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long computeExcessBlobGas(final long parentExcessBlobGas, final long parentBlobGasUsed) {
|
||||
final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed;
|
||||
|
||||
if (currentExcessBlobGas < TARGET_BLOB_GAS_PER_BLOCK) {
|
||||
return 0L;
|
||||
}
|
||||
return currentExcessBlobGas - TARGET_BLOB_GAS_PER_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,12 @@ import org.hyperledger.besu.evm.precompile.SHA256PrecompiledContract;
|
||||
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
import org.apache.tuweni.units.bigints.UInt64;
|
||||
|
||||
/**
|
||||
* Provides various gas cost lookups and calculations used during block processing.
|
||||
@@ -613,37 +615,51 @@ public interface GasCalculator {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the blob gas cost per blob. This is the gas cost for each blob of data that is added to
|
||||
* the block.
|
||||
*
|
||||
* @return the blob gas cost per blob
|
||||
*/
|
||||
default long getBlobGasPerBlob() {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the gas cost given the number of blobs
|
||||
*
|
||||
* @param blobCount the number of blobs
|
||||
* @return the total gas cost
|
||||
*/
|
||||
default long blobGasCost(final int blobCount) {
|
||||
default long blobGasCost(final long blobCount) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the new value for the excess blob gas, given the parent value and the count of new
|
||||
* blobs
|
||||
* Compute the new value for the excess blob gas, given the parent value, the parent blob gas used
|
||||
* and the parent target blobs per block, if present. Used from Cancun onwards. Presence of
|
||||
* parentTargetBlobsPerBlock implies EIP-7442/Prague enabled. Default to Cancun constant target
|
||||
* gas value if parentTargetBlobsPerBlock is not present.
|
||||
*
|
||||
* @param parentExcessBlobGas excess blob gas from the parent
|
||||
* @param newBlobs count of new blobs
|
||||
* @param parentBlobGasUsed blob gas used from the parent
|
||||
* @param parentTargetBlobsPerBlock the optional target blobs per block from the parent
|
||||
* @return the new excess blob gas value
|
||||
*/
|
||||
default long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) {
|
||||
return 0L;
|
||||
}
|
||||
default long computeExcessBlobGas(
|
||||
final long parentExcessBlobGas,
|
||||
final long parentBlobGasUsed,
|
||||
final Optional<UInt64> parentTargetBlobsPerBlock) {
|
||||
final long parentTargetBlobGas =
|
||||
parentTargetBlobsPerBlock
|
||||
.map(blobCount -> blobGasCost(blobCount.toLong()))
|
||||
.orElse(CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK);
|
||||
final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed;
|
||||
|
||||
/**
|
||||
* Compute the new value for the excess blob gas, given the parent value and the blob gas used
|
||||
*
|
||||
* @param parentExcessBlobGas excess blob gas from the parent
|
||||
* @param blobGasUsed blob gas used
|
||||
* @return the new excess blob gas value
|
||||
*/
|
||||
default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) {
|
||||
return 0L;
|
||||
if (currentExcessBlobGas < parentTargetBlobGas) {
|
||||
return 0L;
|
||||
}
|
||||
return currentExcessBlobGas - parentTargetBlobGas;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,15 +47,16 @@ public class AccessListOperationTracer extends EstimateGasOperationTracer {
|
||||
* @return the access list
|
||||
*/
|
||||
public List<AccessListEntry> getAccessList() {
|
||||
final List<AccessListEntry> list = new ArrayList<>();
|
||||
if (warmedUpStorage != null && !warmedUpStorage.isEmpty()) {
|
||||
final List<AccessListEntry> list = new ArrayList<>(warmedUpStorage.size());
|
||||
warmedUpStorage
|
||||
.rowMap()
|
||||
.forEach(
|
||||
(address, storageKeys) ->
|
||||
list.add(new AccessListEntry(address, new ArrayList<>(storageKeys.keySet()))));
|
||||
return list;
|
||||
}
|
||||
return list;
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,31 +17,37 @@ package org.hyperledger.besu.evm.gascalculator;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class CancunGasCalculatorTest {
|
||||
|
||||
private final CancunGasCalculator gasCalculator = new CancunGasCalculator();
|
||||
private final CancunGasCalculator cancunGasCalculator = new CancunGasCalculator();
|
||||
|
||||
@ParameterizedTest(name = "{index} - parent gas {0}, used gas {1}, new excess {2}")
|
||||
@MethodSource("blobGasses")
|
||||
public void shouldCalculateExcessBlobGasCorrectly(
|
||||
final long parentExcess, final long used, final long expected) {
|
||||
assertThat(gasCalculator.computeExcessBlobGas(parentExcess, (int) used)).isEqualTo(expected);
|
||||
final long usedBlobGas = cancunGasCalculator.blobGasCost(used);
|
||||
assertThat(
|
||||
cancunGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, Optional.empty()))
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
|
||||
static Iterable<Arguments> blobGasses() {
|
||||
Iterable<Arguments> blobGasses() {
|
||||
long targetGasPerBlock = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK;
|
||||
return List.of(
|
||||
Arguments.of(0L, 0L, 0L),
|
||||
Arguments.of(targetGasPerBlock, 0L, 0L),
|
||||
Arguments.of(0L, 3, 0L),
|
||||
Arguments.of(1, 3, 1),
|
||||
Arguments.of(targetGasPerBlock, 1, CancunGasCalculator.BLOB_GAS_PER_BLOB),
|
||||
Arguments.of(targetGasPerBlock, 1, cancunGasCalculator.getBlobGasPerBlob()),
|
||||
Arguments.of(targetGasPerBlock, 3, targetGasPerBlock));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,65 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.units.bigints.UInt64;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class PragueGasCalculatorTest {
|
||||
|
||||
private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
|
||||
|
||||
@Test
|
||||
void testPrecompileSize() {
|
||||
PragueGasCalculator subject = new PragueGasCalculator();
|
||||
assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse();
|
||||
assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest(
|
||||
name = "{index} - parent gas {0}, used gas {1}, blob target {2} new excess {3}")
|
||||
@MethodSource("blobGasses")
|
||||
public void shouldCalculateExcessBlobGasCorrectly(
|
||||
final long parentExcess, final long used, final long target, final long expected) {
|
||||
final long usedBlobGas = pragueGasCalculator.blobGasCost(used);
|
||||
assertThat(
|
||||
pragueGasCalculator.computeExcessBlobGas(
|
||||
parentExcess, usedBlobGas, Optional.of(UInt64.valueOf(target))))
|
||||
.isEqualTo(expected);
|
||||
}
|
||||
|
||||
Iterable<Arguments> blobGasses() {
|
||||
long threeBlobTargetGas = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK;
|
||||
long cancunTargetCount = 3;
|
||||
long newTargetCount = 4;
|
||||
|
||||
return List.of(
|
||||
// If blob target count remains at 3
|
||||
Arguments.of(0L, 0L, cancunTargetCount, 0L),
|
||||
Arguments.of(threeBlobTargetGas, 0L, cancunTargetCount, 0L),
|
||||
Arguments.of(0L, cancunTargetCount, cancunTargetCount, 0L),
|
||||
Arguments.of(1L, cancunTargetCount, cancunTargetCount, 1L),
|
||||
Arguments.of(
|
||||
threeBlobTargetGas, 1L, cancunTargetCount, pragueGasCalculator.getBlobGasPerBlob()),
|
||||
Arguments.of(threeBlobTargetGas, 3L, cancunTargetCount, threeBlobTargetGas),
|
||||
// New target count
|
||||
Arguments.of(0L, 0L, newTargetCount, 0L),
|
||||
Arguments.of(threeBlobTargetGas, 0L, newTargetCount, 0L),
|
||||
Arguments.of(newTargetCount, 0L, newTargetCount, 0L),
|
||||
Arguments.of(0L, newTargetCount, newTargetCount, 0L),
|
||||
Arguments.of(1L, newTargetCount, newTargetCount, 1L),
|
||||
Arguments.of(
|
||||
pragueGasCalculator.blobGasCost(newTargetCount),
|
||||
1L,
|
||||
newTargetCount,
|
||||
pragueGasCalculator.getBlobGasPerBlob()),
|
||||
Arguments.of(threeBlobTargetGas, newTargetCount, newTargetCount, threeBlobTargetGas));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ Calculated : ${currentHash}
|
||||
tasks.register('checkAPIChanges', FileStateChecker) {
|
||||
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
|
||||
files = sourceSets.main.allJava.files
|
||||
knownHash = 'dsbVupAvtmZlEeEeVDtk+VrzGFvyKxHgQntaMtOq5TY='
|
||||
knownHash = 'TPCo4SZ61OrJxRAa2SIcAIOAOjVTdRw+UOeHMuiJP84='
|
||||
}
|
||||
check.dependsOn('checkAPIChanges')
|
||||
|
||||
|
||||
@@ -26,27 +26,14 @@ import java.util.Optional;
|
||||
/** Transaction simulation service interface */
|
||||
@Unstable
|
||||
public interface TransactionSimulationService extends BesuService {
|
||||
/**
|
||||
* Simulate transaction execution at the block identified by the hash
|
||||
*
|
||||
* @param transaction tx
|
||||
* @param blockHash the hash of the block
|
||||
* @param operationTracer the tracer
|
||||
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
|
||||
* @return the result of the simulation
|
||||
*/
|
||||
Optional<TransactionSimulationResult> simulate(
|
||||
Transaction transaction,
|
||||
Hash blockHash,
|
||||
OperationTracer operationTracer,
|
||||
boolean isAllowExceedingBalance);
|
||||
|
||||
/**
|
||||
* Simulate transaction execution at the block identified by the hash
|
||||
* Simulate transaction execution at the block identified by the hash if present, otherwise on the
|
||||
* pending block, with optional state overrides that can be applied before the simulation.
|
||||
*
|
||||
* @param transaction tx
|
||||
* @param accountOverrides state overrides to apply to this simulation
|
||||
* @param blockHash the hash of the block
|
||||
* @param maybeBlockHash optional hash of the block, empty to simulate on pending block
|
||||
* @param operationTracer the tracer
|
||||
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
|
||||
* @return the result of the simulation
|
||||
@@ -54,7 +41,77 @@ public interface TransactionSimulationService extends BesuService {
|
||||
Optional<TransactionSimulationResult> simulate(
|
||||
Transaction transaction,
|
||||
Optional<AccountOverrideMap> accountOverrides,
|
||||
Hash blockHash,
|
||||
Optional<Hash> maybeBlockHash,
|
||||
OperationTracer operationTracer,
|
||||
boolean isAllowExceedingBalance);
|
||||
|
||||
/**
|
||||
* Simulate transaction execution at the block identified by the hash if present, otherwise on the
|
||||
* pending block
|
||||
*
|
||||
* @param transaction tx
|
||||
* @param maybeBlockHash optional hash of the block, empty to simulate on pending block
|
||||
* @param operationTracer the tracer
|
||||
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
|
||||
* @return the result of the simulation
|
||||
*/
|
||||
default Optional<TransactionSimulationResult> simulate(
|
||||
final Transaction transaction,
|
||||
final Optional<Hash> maybeBlockHash,
|
||||
final OperationTracer operationTracer,
|
||||
final boolean isAllowExceedingBalance) {
|
||||
return simulate(
|
||||
transaction, Optional.empty(), maybeBlockHash, operationTracer, isAllowExceedingBalance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate transaction execution at the block identified by the hash
|
||||
*
|
||||
* @param transaction tx
|
||||
* @param blockHash then hash of the block
|
||||
* @param operationTracer the tracer
|
||||
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
|
||||
* @return the result of the simulation
|
||||
* @deprecated use {@link #simulate(Transaction, Optional, OperationTracer, boolean)}
|
||||
*/
|
||||
@Deprecated(since = "24.12", forRemoval = true)
|
||||
default Optional<TransactionSimulationResult> simulate(
|
||||
final Transaction transaction,
|
||||
final Hash blockHash,
|
||||
final OperationTracer operationTracer,
|
||||
final boolean isAllowExceedingBalance) {
|
||||
return simulate(
|
||||
transaction,
|
||||
Optional.empty(),
|
||||
Optional.of(blockHash),
|
||||
operationTracer,
|
||||
isAllowExceedingBalance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate transaction execution at the block identified by the hash, with optional state
|
||||
* overrides that can be applied before the simulation.
|
||||
*
|
||||
* @param transaction tx
|
||||
* @param accountOverrides state overrides to apply to this simulation
|
||||
* @param blockHash the hash of the block
|
||||
* @param operationTracer the tracer
|
||||
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
|
||||
* @return the result of the simulation
|
||||
* @deprecated use {@link #simulate(Transaction, Optional, Optional, OperationTracer, boolean)}
|
||||
*/
|
||||
@Deprecated(since = "24.12", forRemoval = true)
|
||||
default Optional<TransactionSimulationResult> simulate(
|
||||
final Transaction transaction,
|
||||
final Optional<AccountOverrideMap> accountOverrides,
|
||||
final Hash blockHash,
|
||||
final OperationTracer operationTracer,
|
||||
final boolean isAllowExceedingBalance) {
|
||||
return simulate(
|
||||
transaction,
|
||||
accountOverrides,
|
||||
Optional.of(blockHash),
|
||||
operationTracer,
|
||||
isAllowExceedingBalance);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user