Merge branch 'main' into zkbesu

# Conflicts:
#	.github/pull_request_template.md
#	.github/workflows/sonarcloud.yml
This commit is contained in:
Fabio Di Fabio
2025-02-03 14:15:40 +01:00
105 changed files with 2156 additions and 602 deletions

View File

@@ -4,11 +4,17 @@
### Breaking Changes
### Upcoming Breaking Changes
### Additions and Improvements
- Adds timestamps to enable Prague hardfork on Sepolia and Holesky test networks. [#8163](https://github.com/hyperledger/besu/pull/8163)
- Add a tx selector to skip txs from the same sender after the first not selected [#8216](https://github.com/hyperledger/besu/pull/8216)
#### Prague
- Add timestamps to enable Prague hardfork on Sepolia and Holesky test networks [#8163](https://github.com/hyperledger/besu/pull/8163)
- Update system call addresses to match [devnet-6](https://github.com/ethereum/execution-spec-tests/releases/) values [#8209](https://github.com/hyperledger/besu/issues/8209)
#### Plugins
- Extend simulate transaction on pending block plugin API [#8174](https://github.com/hyperledger/besu/pull/8174)
### Bug fixes
- Fix the simulation of txs with a future nonce [#8215](https://github.com/hyperledger/besu/pull/8215)
## 25.1.0

View File

@@ -532,6 +532,7 @@ public class BesuNodeFactory {
.jsonRpcTxPool()
.engineRpcEnabled(true)
.jsonRpcDebug()
.dataStorageConfiguration(DataStorageConfiguration.DEFAULT_BONSAI_CONFIG)
.build());
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.dsl.transaction.eth;
import static org.assertj.core.api.Assertions.assertThat;
import static org.web3j.protocol.core.DefaultBlockParameterName.LATEST;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException;
import org.apache.tuweni.bytes.Bytes;
import org.web3j.protocol.core.methods.response.EthGetCode;
public class EthGetCodeTransaction implements Transaction<Bytes> {
private final Account account;
public EthGetCodeTransaction(final Account account) {
this.account = account;
}
@Override
public Bytes execute(final NodeRequests node) {
try {
final EthGetCode result = node.eth().ethGetCode(account.getAddress(), LATEST).send();
assertThat(result).isNotNull();
assertThat(result.hasError()).isFalse();
return Bytes.fromHexString(result.getCode());
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -43,6 +43,10 @@ public class EthTransactions {
return new EthGetBalanceTransaction(account);
}
public EthGetCodeTransaction getCode(final Account account) {
return new EthGetCodeTransaction(account);
}
public EthGetBalanceAtBlockTransaction getBalanceAtBlock(
final Account account, final BigInteger block) {
return new EthGetBalanceAtBlockTransaction(account, block);

View File

@@ -46,6 +46,9 @@ public class CodeDelegationTransactionAcceptanceTest extends AcceptanceTestBase
public static final Address SEND_ALL_ETH_CONTRACT_ADDRESS =
Address.fromHexStringStrict("0000000000000000000000000000000000009999");
public static final Address ALWAYS_REVERT_CONTRACT_ADDRESS =
Address.fromHexStringStrict("0000000000000000000000000000000000000666");
private final Account authorizer =
accounts.createAccount(
Address.fromHexStringStrict("8da48afC965480220a3dB9244771bd3afcB5d895"));
@@ -232,4 +235,124 @@ public class CodeDelegationTransactionAcceptanceTest extends AcceptanceTestBase
assertThat(otherAccountBalanceAfterFirstTx.add(BigInteger.ONE))
.isEqualTo(otherAccountBalanceAfterSecondTx);
}
/**
* EIP-7702 code delegation should be persisted even if the transaction that contains the
* authorization is reverted.
*/
@Test
public void shouldPersistCodeDelegationAfterRevert() throws IOException {
final long GAS_LIMIT = 1_000_000L;
// check the authorizer has no code before the transaction
final Bytes authorizerCodeBeforeCodeDelegation =
besuNode.execute(ethTransactions.getCode(authorizer));
assertThat(authorizerCodeBeforeCodeDelegation).isEqualTo(Bytes.EMPTY);
// valid 7702 code delegation to SEND_ALL_ETH_CONTRACT_ADDRESS
final CodeDelegation codeDelegation =
org.hyperledger.besu.ethereum.core.CodeDelegation.builder()
.chainId(BigInteger.valueOf(20211))
.nonce(0L)
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));
// the transaction will revert, because the to address is a contract that always reverts
final Transaction tx =
Transaction.builder()
.type(TransactionType.DELEGATE_CODE)
.chainId(BigInteger.valueOf(20211))
.nonce(0)
.maxPriorityFeePerGas(Wei.of(1_000_000_000))
.maxFeePerGas(Wei.fromHexString("0x02540BE400"))
.gasLimit(GAS_LIMIT)
.to(ALWAYS_REVERT_CONTRACT_ADDRESS)
.value(Wei.ZERO)
.payload(Bytes.EMPTY)
.codeDelegations(List.of(codeDelegation))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(
TRANSACTION_SPONSOR_PRIVATE_KEY.toUnsignedBigInteger())));
// include the tx in the next block
final String txHash =
besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString()));
testHelper.buildNewBlock();
// check that the transaction was included and has indeed reverted
Optional<TransactionReceipt> maybeTransactionReceipt =
besuNode.execute(ethTransactions.getTransactionReceipt(txHash));
assertThat(maybeTransactionReceipt).isPresent();
assertThat(maybeTransactionReceipt.get().getStatus()).isEqualTo("0x0");
// check the authorizer has the code delegation after the transaction even though it has
// reverted
final Bytes expectedCode =
Bytes.concatenate(Bytes.fromHexString("ef0100"), SEND_ALL_ETH_CONTRACT_ADDRESS);
final Bytes authorizerCode = besuNode.execute(ethTransactions.getCode(authorizer));
assertThat(authorizerCode).isEqualTo(expectedCode);
}
/**
* EIP-7702 code delegation should be persisted even if the transaction that contains the
* authorization is reverted and the transaction sender is the same as the code delegation
* authorizer.
*/
@Test
public void shouldPersistCodeDelegationAfterRevertWhenSelfSponsored() throws IOException {
final long GAS_LIMIT = 1_000_000L;
// check the authorizer has no code before the transaction
final Bytes authorizerCodeBeforeCodeDelegation =
besuNode.execute(ethTransactions.getCode(authorizer));
assertThat(authorizerCodeBeforeCodeDelegation).isEqualTo(Bytes.EMPTY);
// valid 7702 code delegation to SEND_ALL_ETH_CONTRACT_ADDRESS
final CodeDelegation codeDelegation =
org.hyperledger.besu.ethereum.core.CodeDelegation.builder()
.chainId(BigInteger.valueOf(20211))
.nonce(1L)
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));
// the transaction will revert, because the to address is a contract that always reverts
final Transaction tx =
Transaction.builder()
.type(TransactionType.DELEGATE_CODE)
.chainId(BigInteger.valueOf(20211))
.nonce(0)
.maxPriorityFeePerGas(Wei.of(1_000_000_000))
.maxFeePerGas(Wei.fromHexString("0x02540BE400"))
.gasLimit(GAS_LIMIT)
.to(ALWAYS_REVERT_CONTRACT_ADDRESS)
.value(Wei.ZERO)
.payload(Bytes.EMPTY)
.codeDelegations(List.of(codeDelegation))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));
// include the tx in the next block
final String txHash =
besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString()));
testHelper.buildNewBlock();
// check that the transaction was included and has indeed reverted
Optional<TransactionReceipt> maybeTransactionReceipt =
besuNode.execute(ethTransactions.getTransactionReceipt(txHash));
assertThat(maybeTransactionReceipt).isPresent();
assertThat(maybeTransactionReceipt.get().getStatus()).isEqualTo("0x0");
// check the authorizer has the code delegation after the transaction even though it has
// reverted
final Bytes expectedCode =
Bytes.concatenate(Bytes.fromHexString("ef0100"), SEND_ALL_ETH_CONTRACT_ADDRESS);
final Bytes authorizerCode = besuNode.execute(ethTransactions.getCode(authorizer));
assertThat(authorizerCode).isEqualTo(expectedCode);
}
}

View File

@@ -57,6 +57,13 @@
"privateKey": "11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1",
"balance": "90000000000000000000000"
},
"0x0000000000000000000000000000000000000666": {
"comment": "Contract reverts immediately when called",
"balance": "0",
"code": "5F5FFD",
"codeDecompiled": "PUSH0 PUSH0 REVERT",
"storage": {}
},
"0x0000000000000000000000000000000000009999": {
"comment": "Contract sends all its Ether to the address provided as a call data.",
"balance": "0",

View File

@@ -69,6 +69,7 @@ import org.hyperledger.besu.cli.options.SynchronizerOptions;
import org.hyperledger.besu.cli.options.TransactionPoolOptions;
import org.hyperledger.besu.cli.options.storage.DataStorageOptions;
import org.hyperledger.besu.cli.options.storage.DiffBasedSubStorageOptions;
import org.hyperledger.besu.cli.options.unstable.QBFTOptions;
import org.hyperledger.besu.cli.presynctasks.PreSynchronizationTaskRunner;
import org.hyperledger.besu.cli.presynctasks.PrivateDatabaseMigrationPreSyncTask;
import org.hyperledger.besu.cli.subcommands.PasswordSubCommand;
@@ -301,6 +302,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final EvmOptions unstableEvmOptions = EvmOptions.create();
private final IpcOptions unstableIpcOptions = IpcOptions.create();
private final ChainPruningOptions unstableChainPruningOptions = ChainPruningOptions.create();
private final QBFTOptions unstableQbftOptions = QBFTOptions.create();
// stable CLI options
final DataStorageOptions dataStorageOptions = DataStorageOptions.create();
@@ -1162,6 +1164,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.put("EVM Options", unstableEvmOptions)
.put("IPC Options", unstableIpcOptions)
.put("Chain Data Pruning Options", unstableChainPruningOptions)
.put("QBFT Options", unstableQbftOptions)
.build();
UnstableOptionsSubCommand.createUnstableOptions(commandLine, unstableOptions);
@@ -1794,6 +1797,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.clock(Clock.systemUTC())
.isRevertReasonEnabled(isRevertReasonEnabled)
.storageProvider(storageProvider)
.isEarlyRoundChangeEnabled(unstableQbftOptions.isEarlyRoundChangeEnabled())
.gasLimitCalculator(
miningParametersSupplier.get().getTargetGasLimit().isPresent()
? new FrontierTargetingGasLimitCalculator()

View File

@@ -0,0 +1,49 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.unstable;
import picocli.CommandLine;
/** Handles configuration options for QBFT consensus */
public class QBFTOptions {
/** Default constructor */
private QBFTOptions() {}
/**
* Create a new instance of QBFTOptions
*
* @return a new instance of QBFTOptions
*/
public static QBFTOptions create() {
return new QBFTOptions();
}
@CommandLine.Option(
names = {"--Xqbft-enable-early-round-change"},
description =
"Enable early round change upon receiving f+1 valid future Round Change messages from different validators (experimental)",
hidden = true)
private boolean enableEarlyRoundChange = false;
/**
* Is early round change enabled boolean.
*
* @return true if early round change is enabled
*/
public boolean isEarlyRoundChangeEnabled() {
return enableEarlyRoundChange;
}
}

View File

@@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration;
import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration.MutableInitValues;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract;
import org.hyperledger.besu.metrics.MetricsService;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
@@ -206,6 +207,7 @@ public class BlocksSubCommand implements Runnable {
}
LOG.info("Import {} block data from {} files", format, blockImportFiles.size());
final Optional<MetricsService> metricsService = initMetrics(parentCommand);
KZGPointEvalPrecompiledContract.init();
try (final BesuController controller = createController()) {
for (final Path path : blockImportFiles) {

View File

@@ -219,6 +219,9 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
/** The transaction simulator */
protected TransactionSimulator transactionSimulator;
/** When enabled, round changes on f+1 RC messages from higher rounds */
protected boolean isEarlyRoundChangeEnabled = false;
/** Instantiates a new Besu controller builder. */
protected BesuControllerBuilder() {}
@@ -553,6 +556,17 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
return this;
}
/**
* check if early round change is enabled when f+1 RC messages from higher rounds are received
*
* @param isEarlyRoundChangeEnabled whether to enable early round change
* @return the besu controller
*/
public BesuControllerBuilder isEarlyRoundChangeEnabled(final boolean isEarlyRoundChangeEnabled) {
this.isEarlyRoundChangeEnabled = isEarlyRoundChangeEnabled;
return this;
}
/**
* Build besu controller.
*

View File

@@ -245,23 +245,28 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
final MessageFactory messageFactory = new MessageFactory(nodeKey);
QbftBlockHeightManagerFactory qbftBlockHeightManagerFactory =
new QbftBlockHeightManagerFactory(
finalState,
new QbftRoundFactory(
finalState,
protocolContext,
bftProtocolSchedule,
minedBlockObservers,
messageValidatorFactory,
messageFactory,
bftExtraDataCodec().get()),
messageValidatorFactory,
messageFactory,
new ValidatorModeTransitionLogger(qbftForksSchedule));
qbftBlockHeightManagerFactory.isEarlyRoundChangeEnabled(isEarlyRoundChangeEnabled);
final BftEventHandler qbftController =
new QbftController(
blockchain,
finalState,
new QbftBlockHeightManagerFactory(
finalState,
new QbftRoundFactory(
finalState,
protocolContext,
bftProtocolSchedule,
minedBlockObservers,
messageValidatorFactory,
messageFactory,
bftExtraDataCodec().get()),
messageValidatorFactory,
messageFactory,
new ValidatorModeTransitionLogger(qbftForksSchedule)),
qbftBlockHeightManagerFactory,
gossiper,
duplicateMessageTracker,
futureMessageBuffer,

View File

@@ -280,6 +280,7 @@ public abstract class CommandTestAbstract {
when(mockControllerBuilder.isRevertReasonEnabled(false)).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.isParallelTxProcessingEnabled(false))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.isEarlyRoundChangeEnabled(false)).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.storageProvider(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.gasLimitCalculator(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.requiredBlocks(any())).thenReturn(mockControllerBuilder);

View File

@@ -43,6 +43,16 @@ public class BftHelpers {
return Util.fastDivCeiling(2 * validatorCount, 3);
}
/**
* Calculate required future RC messages count quorum for a round change.
*
* @param validatorCount the validator count
* @return Required number of future round change messages to reach quorum for a round change.
*/
public static int calculateRequiredFutureRCQuorum(final int validatorCount) {
return (validatorCount - 1) / 3 + 1;
}
/**
* Prepare message count for quorum.
*

View File

@@ -83,8 +83,7 @@ public class RoundTimer {
// Once we are up to round 2 start logging round expiries
if (round.getRoundNumber() >= 2) {
LOG.info(
"BFT round {} expired. Moved to round {} which will expire in {} seconds",
round.getRoundNumber() - 1,
"Moved to round {} which will expire in {} seconds",
round.getRoundNumber(),
(expiryTime / 1000));
}

View File

@@ -63,4 +63,39 @@ public class BftHelpersTest {
public void calculateRequiredValidatorQuorum20Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredValidatorQuorum(20)).isEqualTo(14);
}
@Test
public void calculateRequiredFutureRCQuorum4Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(4)).isEqualTo(2);
}
@Test
public void calculateRequiredFutureRCQuorum6Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(6)).isEqualTo(2);
}
@Test
public void calculateRequiredFutureRCQuorum7Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(7)).isEqualTo(3);
}
@Test
public void calculateRequiredFutureRCQuorum9Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(9)).isEqualTo(3);
}
@Test
public void calculateRequiredFutureRCQuorum10Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(10)).isEqualTo(4);
}
@Test
public void calculateRequiredFutureRCQuorum13Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(13)).isEqualTo(5);
}
@Test
public void calculateRequiredFutureRCQuorum15Validator() {
Assertions.assertThat(BftHelpers.calculateRequiredFutureRCQuorum(15)).isEqualTo(5);
}
}

View File

@@ -68,6 +68,7 @@ public class QbftBlockHeightManager implements BaseQbftBlockHeightManager {
private Optional<PreparedCertificate> latestPreparedCertificate = Optional.empty();
private Optional<QbftRound> currentRound = Optional.empty();
private boolean isEarlyRoundChangeEnabled = false;
/**
* Instantiates a new Qbft block height manager.
@@ -115,6 +116,39 @@ public class QbftBlockHeightManager implements BaseQbftBlockHeightManager {
finalState.getBlockTimer().startTimer(roundIdentifier, parentHeader);
}
/**
* Instantiates a new Qbft block height manager. Secondary constructor with early round change
* option.
*
* @param parentHeader the parent header
* @param finalState the final state
* @param roundChangeManager the round change manager
* @param qbftRoundFactory the qbft round factory
* @param clock the clock
* @param messageValidatorFactory the message validator factory
* @param messageFactory the message factory
* @param isEarlyRoundChangeEnabled enable round change when f+1 RC messages are received
*/
public QbftBlockHeightManager(
final BlockHeader parentHeader,
final BftFinalState finalState,
final RoundChangeManager roundChangeManager,
final QbftRoundFactory qbftRoundFactory,
final Clock clock,
final MessageValidatorFactory messageValidatorFactory,
final MessageFactory messageFactory,
final boolean isEarlyRoundChangeEnabled) {
this(
parentHeader,
finalState,
roundChangeManager,
qbftRoundFactory,
clock,
messageValidatorFactory,
messageFactory);
this.isEarlyRoundChangeEnabled = isEarlyRoundChangeEnabled;
}
@Override
public void handleBlockTimerExpiry(final ConsensusRoundIdentifier roundIdentifier) {
if (currentRound.isPresent()) {
@@ -227,23 +261,36 @@ public class QbftBlockHeightManager implements BaseQbftBlockHeightManager {
return;
}
doRoundChange(qbftRound.getRoundIdentifier().getRoundNumber() + 1);
}
private synchronized void doRoundChange(final int newRoundNumber) {
if (currentRound.isPresent()
&& currentRound.get().getRoundIdentifier().getRoundNumber() >= newRoundNumber) {
return;
}
LOG.debug(
"Round has expired, creating PreparedCertificate and notifying peers. round={}",
qbftRound.getRoundIdentifier());
"Round has expired or changing based on RC quorum, creating PreparedCertificate and notifying peers. round={}",
currentRound.get().getRoundIdentifier());
final Optional<PreparedCertificate> preparedCertificate =
qbftRound.constructPreparedCertificate();
currentRound.get().constructPreparedCertificate();
if (preparedCertificate.isPresent()) {
latestPreparedCertificate = preparedCertificate;
}
startNewRound(qbftRound.getRoundIdentifier().getRoundNumber() + 1);
qbftRound = currentRound.get();
startNewRound(newRoundNumber);
if (currentRound.isEmpty()) {
LOG.info("Failed to start round ");
return;
}
QbftRound qbftRoundNew = currentRound.get();
try {
final RoundChange localRoundChange =
messageFactory.createRoundChange(
qbftRound.getRoundIdentifier(), latestPreparedCertificate);
qbftRoundNew.getRoundIdentifier(), latestPreparedCertificate);
// Its possible the locally created RoundChange triggers the transmission of a NewRound
// message - so it must be handled accordingly.
@@ -252,7 +299,7 @@ public class QbftBlockHeightManager implements BaseQbftBlockHeightManager {
LOG.warn("Failed to create signed RoundChange message.", e);
}
transmitter.multicastRoundChange(qbftRound.getRoundIdentifier(), latestPreparedCertificate);
transmitter.multicastRoundChange(qbftRoundNew.getRoundIdentifier(), latestPreparedCertificate);
}
@Override
@@ -333,24 +380,55 @@ public class QbftBlockHeightManager implements BaseQbftBlockHeightManager {
final Optional<Collection<RoundChange>> result =
roundChangeManager.appendRoundChangeMessage(message);
if (result.isPresent()) {
LOG.debug(
"Received sufficient RoundChange messages to change round to targetRound={}",
targetRound);
if (messageAge == MessageAge.FUTURE_ROUND) {
startNewRound(targetRound.getRoundNumber());
}
final RoundChangeArtifacts roundChangeMetadata = RoundChangeArtifacts.create(result.get());
if (finalState.isLocalNodeProposerForRound(targetRound)) {
if (currentRound.isEmpty()) {
startNewRound(0);
if (!isEarlyRoundChangeEnabled) {
if (result.isPresent()) {
LOG.debug(
"Received sufficient RoundChange messages to change round to targetRound={}",
targetRound);
if (messageAge == MessageAge.FUTURE_ROUND) {
startNewRound(targetRound.getRoundNumber());
}
final RoundChangeArtifacts roundChangeMetadata = RoundChangeArtifacts.create(result.get());
if (finalState.isLocalNodeProposerForRound(targetRound)) {
if (currentRound.isEmpty()) {
startNewRound(0);
}
currentRound
.get()
.startRoundWith(roundChangeMetadata, TimeUnit.MILLISECONDS.toSeconds(clock.millis()));
}
}
} else {
if (currentRound.isEmpty()) {
startNewRound(0);
}
int currentRoundNumber = currentRound.get().getRoundIdentifier().getRoundNumber();
// If this node is proposer for the current round, check if quorum is achieved for RC messages
// aiming this round
if (targetRound.getRoundNumber() == currentRoundNumber
&& finalState.isLocalNodeProposerForRound(targetRound)
&& result.isPresent()) {
final RoundChangeArtifacts roundChangeMetadata = RoundChangeArtifacts.create(result.get());
currentRound
.get()
.startRoundWith(roundChangeMetadata, TimeUnit.MILLISECONDS.toSeconds(clock.millis()));
}
// check if f+1 RC messages for future rounds are received
QbftRound qbftRound = currentRound.get();
Optional<Integer> nextHigherRound =
roundChangeManager.futureRCQuorumReceived(qbftRound.getRoundIdentifier());
if (nextHigherRound.isPresent()) {
LOG.info(
"Received sufficient RoundChange messages to change round to targetRound={}",
nextHigherRound.get());
doRoundChange(nextHigherRound.get());
}
}
}

View File

@@ -34,6 +34,7 @@ public class QbftBlockHeightManagerFactory {
private final MessageValidatorFactory messageValidatorFactory;
private final MessageFactory messageFactory;
private final ValidatorModeTransitionLogger validatorModeTransitionLogger;
private boolean isEarlyRoundChangeEnabled = false;
/**
* Instantiates a new Qbft block height manager factory.
@@ -75,22 +76,60 @@ public class QbftBlockHeightManagerFactory {
}
}
/**
* Sets early round change enabled.
*
* @param isEarlyRoundChangeEnabled the is early round change enabled
*/
public void isEarlyRoundChangeEnabled(final boolean isEarlyRoundChangeEnabled) {
this.isEarlyRoundChangeEnabled = isEarlyRoundChangeEnabled;
}
private BaseQbftBlockHeightManager createNoOpBlockHeightManager(final BlockHeader parentHeader) {
return new NoOpBlockHeightManager(parentHeader);
}
private BaseQbftBlockHeightManager createFullBlockHeightManager(final BlockHeader parentHeader) {
return new QbftBlockHeightManager(
parentHeader,
finalState,
new RoundChangeManager(
BftHelpers.calculateRequiredValidatorQuorum(finalState.getValidators().size()),
messageValidatorFactory.createRoundChangeMessageValidator(
parentHeader.getNumber() + 1L, parentHeader),
finalState.getLocalAddress()),
roundFactory,
finalState.getClock(),
messageValidatorFactory,
messageFactory);
QbftBlockHeightManager qbftBlockHeightManager;
RoundChangeManager roundChangeManager;
if (isEarlyRoundChangeEnabled) {
roundChangeManager =
new RoundChangeManager(
BftHelpers.calculateRequiredValidatorQuorum(finalState.getValidators().size()),
BftHelpers.calculateRequiredFutureRCQuorum(finalState.getValidators().size()),
messageValidatorFactory.createRoundChangeMessageValidator(
parentHeader.getNumber() + 1L, parentHeader),
finalState.getLocalAddress());
qbftBlockHeightManager =
new QbftBlockHeightManager(
parentHeader,
finalState,
roundChangeManager,
roundFactory,
finalState.getClock(),
messageValidatorFactory,
messageFactory,
true);
} else {
roundChangeManager =
new RoundChangeManager(
BftHelpers.calculateRequiredValidatorQuorum(finalState.getValidators().size()),
messageValidatorFactory.createRoundChangeMessageValidator(
parentHeader.getNumber() + 1L, parentHeader),
finalState.getLocalAddress());
qbftBlockHeightManager =
new QbftBlockHeightManager(
parentHeader,
finalState,
roundChangeManager,
roundFactory,
finalState.getClock(),
messageValidatorFactory,
messageFactory);
}
return qbftBlockHeightManager;
}
}

View File

@@ -22,6 +22,7 @@ import org.hyperledger.besu.datatypes.Address;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
@@ -75,7 +76,7 @@ public class RoundChangeManager {
*
* @return the boolean
*/
public boolean roundChangeReady() {
public boolean roundChangeQuorumReceived() {
return receivedMessages.size() >= quorum && !actioned;
}
@@ -85,7 +86,7 @@ public class RoundChangeManager {
* @return the collection
*/
public Collection<RoundChange> createRoundChangeCertificate() {
if (roundChangeReady()) {
if (roundChangeQuorumReceived()) {
actioned = true;
return receivedMessages.values();
} else {
@@ -104,6 +105,7 @@ public class RoundChangeManager {
private final Map<Address, ConsensusRoundIdentifier> roundSummary = Maps.newHashMap();
private final long quorum;
private long rcQuorum;
private final RoundChangeMessageValidator roundChangeMessageValidator;
private final Address localAddress;
@@ -123,6 +125,23 @@ public class RoundChangeManager {
this.localAddress = localAddress;
}
/**
* Instantiates a new Round change manager.
*
* @param quorum the quorum
* @param rcQuorum quorum for round change messages
* @param roundChangeMessageValidator the round change message validator
* @param localAddress this node's address
*/
public RoundChangeManager(
final long quorum,
final long rcQuorum,
final RoundChangeMessageValidator roundChangeMessageValidator,
final Address localAddress) {
this(quorum, roundChangeMessageValidator, localAddress);
this.rcQuorum = rcQuorum;
}
/**
* Store the latest round for a node, and if chain is stalled log a summary of which round each
* address is on
@@ -130,6 +149,10 @@ public class RoundChangeManager {
* @param message the round-change message that has just been received
*/
public void storeAndLogRoundChangeSummary(final RoundChange message) {
if (!isMessageValid(message)) {
LOG.info("RoundChange message is invalid .");
return;
}
roundSummary.put(message.getAuthor(), message.getRoundIdentifier());
if (roundChangeCache.keySet().stream()
.findFirst()
@@ -147,6 +170,39 @@ public class RoundChangeManager {
}
}
/**
* Checks if a quorum of round change messages has been received for a round higher than the
* current round
*
* @param currentRoundIdentifier the current round identifier
* @return the next higher round number if quorum is reached, otherwise empty Optional
*/
public Optional<Integer> futureRCQuorumReceived(
final ConsensusRoundIdentifier currentRoundIdentifier) {
// Iterate through elements of round summary, identify ones with round number higher than
// current,
// tracking minimum of those and return the next higher round number if quorum is reached
// Filter out entries with round number greater than current round
// and collect their round numbers
Map<Address, Integer> higherRounds =
roundSummary.entrySet().stream()
.filter(entry -> isAFutureRound(entry.getValue(), currentRoundIdentifier))
.collect(
Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getRoundNumber()));
LOG.debug("Higher rounds size ={} rcquorum = {}", higherRounds.size(), rcQuorum);
// Check if we have at least f + 1 validators at higher rounds
if (higherRounds.size() >= rcQuorum) {
// Find the minimum round that is greater than the current round
return Optional.of(higherRounds.values().stream().min(Integer::compareTo).orElseThrow());
}
// If quorum is not reached, return empty Optional
return Optional.empty();
}
/**
* Adds the round message to this manager and return a certificate if it passes the threshold
*
@@ -163,7 +219,7 @@ public class RoundChangeManager {
final RoundChangeStatus roundChangeStatus = storeRoundChangeMessage(msg);
if (roundChangeStatus.roundChangeReady()) {
if (roundChangeStatus.roundChangeQuorumReceived()) {
return Optional.of(roundChangeStatus.createRoundChangeCertificate());
}
@@ -198,4 +254,9 @@ public class RoundChangeManager {
final ConsensusRoundIdentifier left, final ConsensusRoundIdentifier right) {
return left.getRoundNumber() < right.getRoundNumber();
}
private boolean isAFutureRound(
final ConsensusRoundIdentifier left, final ConsensusRoundIdentifier right) {
return left.getRoundNumber() > right.getRoundNumber();
}
}

View File

@@ -53,6 +53,7 @@ import org.hyperledger.besu.consensus.qbft.core.payload.MessageFactory;
import org.hyperledger.besu.consensus.qbft.core.validation.FutureRoundProposalMessageValidator;
import org.hyperledger.besu.consensus.qbft.core.validation.MessageValidator;
import org.hyperledger.besu.consensus.qbft.core.validation.MessageValidatorFactory;
import org.hyperledger.besu.consensus.qbft.core.validation.RoundChangeMessageValidator;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
@@ -138,7 +139,7 @@ public class QbftBlockHeightManagerTest {
@BeforeEach
public void setup() {
for (int i = 0; i < 3; i++) {
for (int i = 0; i <= 3; i++) {
final NodeKey nodeKey = NodeKeyUtils.generate();
validators.add(Util.publicKeyToAddress(nodeKey.getPublicKey()));
validatorMessageFactory.add(new MessageFactory(nodeKey));
@@ -602,4 +603,42 @@ public class QbftBlockHeightManagerTest {
verify(blockTimer, times(0)).getEmptyBlockPeriodSeconds();
verify(blockTimer, times(0)).getBlockPeriodSeconds();
}
@Test
public void roundChangeTriggeredUponReceivingFPlusOneRoundChanges() {
final ConsensusRoundIdentifier futureRoundIdentifier1 = createFrom(roundIdentifier, 0, +2);
final ConsensusRoundIdentifier futureRoundIdentifier2 = createFrom(roundIdentifier, 0, +3);
final RoundChange roundChange1 =
validatorMessageFactory.get(0).createRoundChange(futureRoundIdentifier1, Optional.empty());
final RoundChange roundChange2 =
validatorMessageFactory.get(1).createRoundChange(futureRoundIdentifier2, Optional.empty());
RoundChangeMessageValidator roundChangeMessageValidator =
mock(RoundChangeMessageValidator.class);
when(roundChangeMessageValidator.validate(any())).thenReturn(true);
// Instantiate the real RoundChangeManager
final RoundChangeManager roundChangeManager =
new RoundChangeManager(3, 2, roundChangeMessageValidator, validators.get(2));
when(finalState.isLocalNodeProposerForRound(any())).thenReturn(false);
final QbftBlockHeightManager manager =
new QbftBlockHeightManager(
headerTestFixture.buildHeader(),
finalState,
roundChangeManager,
roundFactory,
clock,
messageValidatorFactory,
validatorMessageFactory.get(2),
true); // Enable early round change
manager.handleRoundChangePayload(roundChange1);
manager.handleRoundChangePayload(roundChange2);
verify(roundFactory, times(1))
.createNewRound(any(), eq(futureRoundIdentifier1.getRoundNumber()));
}
}

View File

@@ -52,8 +52,25 @@ public enum RequestType {
case 0x01 -> WITHDRAWAL;
case 0x02 -> CONSOLIDATION;
default ->
throw new IllegalArgumentException(
throw new InvalidRequestTypeException(
String.format("Unsupported request type: 0x%02X", serializedTypeValue));
};
}
/**
* Exception thrown when an invalid request type is encountered.
*
* <p>This exception is thrown when a serialized type value does not correspond to any {@link
* RequestType}.
*/
public static class InvalidRequestTypeException extends IllegalArgumentException {
/**
* Constructs an {@link InvalidRequestTypeException} with the specified detail message.
*
* @param message the detail message.
*/
public InvalidRequestTypeException(final String message) {
super(message);
}
}
}

View File

@@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.datatypes;
import static com.google.common.base.Preconditions.checkState;
import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter;
import java.util.Map;
@@ -35,6 +37,7 @@ public class StateOverride {
private final Optional<Wei> balance;
private final Optional<Long> nonce;
private final Optional<String> code;
private final Optional<Map<String, String>> state;
private final Optional<Map<String, String>> stateDiff;
private final Optional<Address> movePrecompileToAddress;
@@ -42,11 +45,13 @@ public class StateOverride {
final Optional<Wei> balance,
final Optional<Long> nonce,
final Optional<String> code,
final Optional<Map<String, String>> state,
final Optional<Map<String, String>> stateDiff,
final Optional<Address> movePrecompileToAddress) {
this.balance = balance;
this.nonce = nonce;
this.code = code;
this.state = state;
this.stateDiff = stateDiff;
this.movePrecompileToAddress = movePrecompileToAddress;
}
@@ -83,6 +88,15 @@ public class StateOverride {
*
* @return the state override map if present
*/
public Optional<Map<String, String>> getState() {
return state;
}
/**
* Gets the state diff override map
*
* @return the state diff override map if present
*/
public Optional<Map<String, String>> getStateDiff() {
return stateDiff;
}
@@ -102,6 +116,7 @@ public class StateOverride {
private Optional<Wei> balance = Optional.empty();
private Optional<Long> nonce = Optional.empty();
private Optional<String> code = Optional.empty();
private Optional<Map<String, String>> state = Optional.empty();
private Optional<Map<String, String>> stateDiff = Optional.empty();
private Optional<Address> movePrecompileToAddress = Optional.empty();
@@ -141,6 +156,17 @@ public class StateOverride {
return this;
}
/**
* Sets the state override
*
* @param state the map of state overrides
* @return the builder
*/
public Builder withState(final Map<String, String> state) {
this.state = Optional.ofNullable(state);
return this;
}
/**
* Sets the state diff override
*
@@ -169,7 +195,8 @@ public class StateOverride {
* @return account override
*/
public StateOverride build() {
return new StateOverride(balance, nonce, code, stateDiff, movePrecompileToAddress);
checkState(state.isEmpty() || stateDiff.isEmpty(), "Cannot set both state and stateDiff");
return new StateOverride(balance, nonce, code, state, stateDiff, movePrecompileToAddress);
}
}
@@ -200,12 +227,13 @@ public class StateOverride {
return balance.equals(stateOverride.balance)
&& nonce.equals(stateOverride.nonce)
&& code.equals(stateOverride.code)
&& state.equals(stateOverride.state)
&& stateDiff.equals(stateOverride.stateDiff);
}
@Override
public int hashCode() {
return Objects.hash(balance, nonce, code, stateDiff);
return Objects.hash(balance, nonce, code, state, stateDiff);
}
@Override
@@ -217,6 +245,8 @@ public class StateOverride {
+ nonce
+ ", code="
+ code
+ ", state="
+ state
+ ", stateDiff="
+ stateDiff
+ ", movePrecompileToAddress="

View File

@@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
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.CallParameter;
@@ -163,14 +162,9 @@ public class PendingStateAdapter extends AdapterBase {
final CallParameter param =
new CallParameter(from, to, gasParam, gasPriceParam, valueParam, data);
ImmutableTransactionValidationParams.Builder transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator());
transactionValidationParams.isAllowExceedingBalance(true);
return transactionSimulator.process(
param,
transactionValidationParams.build(),
TransactionValidationParams.transactionSimulatorAllowExceedingBalanceAndFutureNonce(),
OperationTracer.NO_TRACING,
(mutableWorldState, transactionSimulatorResult) ->
transactionSimulatorResult.map(

View File

@@ -102,7 +102,7 @@ public abstract class AbstractDebugTraceBlock implements JsonRpcMethod {
block ->
Tracer.processTracing(
getBlockchainQueries(),
block.getHash(),
Optional.of(block.getHeader()),
traceableState -> {
List<DebugTraceTransactionResult> tracesList =
Collections.synchronizedList(new ArrayList<>());

View File

@@ -210,8 +210,8 @@ public abstract class AbstractEstimateGas extends AbstractBlockParameterMethod {
final boolean isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
return isAllowExceedingBalance
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
: TransactionValidationParams.transactionSimulator();
? TransactionValidationParams.transactionSimulatorAllowExceedingBalanceAndFutureNonce()
: TransactionValidationParams.transactionSimulatorAllowFutureNonce();
}
@VisibleForTesting

View File

@@ -39,6 +39,13 @@ import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import java.util.Optional;
public class DebugTraceCall extends AbstractTraceCall {
private static final TransactionValidationParams TRANSACTION_VALIDATION_PARAMS =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowFutureNonce(true)
.isAllowExceedingBalance(true)
.allowUnderpriced(true)
.build();
public DebugTraceCall(
final BlockchainQueries blockchainQueries,
@@ -103,10 +110,6 @@ public class DebugTraceCall extends AbstractTraceCall {
@Override
protected TransactionValidationParams buildTransactionValidationParams() {
return ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(true)
.allowUnderpriced(true)
.build();
return TRANSACTION_VALIDATION_PARAMS;
}
}

View File

@@ -169,8 +169,8 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
isAllowExceedingBalance = !callParams.isMaybeStrict().orElse(Boolean.FALSE);
}
return isAllowExceedingBalance
? TransactionValidationParams.transactionSimulatorAllowExceedingBalance()
: TransactionValidationParams.transactionSimulator();
? TransactionValidationParams.transactionSimulatorAllowExceedingBalanceAndFutureNonce()
: TransactionValidationParams.transactionSimulatorAllowFutureNonce();
}
private boolean isAllowExceedingBalanceAutoSelection(

View File

@@ -202,13 +202,15 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
final Optional<List<Request>> maybeRequests;
try {
maybeRequests = extractRequests(maybeRequestsParam);
} catch (RuntimeException ex) {
} catch (RequestType.InvalidRequestTypeException ex) {
return respondWithInvalid(
reqId,
blockParam,
mergeCoordinator.getLatestValidAncestor(blockParam.getParentHash()).orElse(null),
INVALID,
"Invalid execution requests");
} catch (Exception ex) {
return new JsonRpcErrorResponse(reqId, RpcErrorType.INVALID_EXECUTION_REQUESTS_PARAMS);
}
if (!getRequestsValidator(
@@ -364,7 +366,7 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
.mapToInt(List::size)
.sum(),
lastExecutionTime / 1000.0,
executionResult.getNbParallelizedTransations());
executionResult.getNbParallelizedTransactions());
return respondWith(reqId, blockParam, newBlockHeader.getHash(), VALID);
} else {
if (executionResult.causedBy().isPresent()) {
@@ -591,14 +593,17 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
if (maybeRequestsParam.isEmpty()) {
return Optional.empty();
}
return maybeRequestsParam.map(
requests ->
requests.stream()
.map(
s -> {
final Bytes request = Bytes.fromHexString(s);
return new Request(RequestType.of(request.get(0)), request.slice(1));
final Bytes requestData = request.slice(1);
if (requestData.isEmpty()) {
throw new IllegalArgumentException("Request data cannot be empty");
}
return new Request(RequestType.of(request.get(0)), requestData);
})
.collect(Collectors.toList()));
}
@@ -607,7 +612,7 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
final Block block,
final int blobCount,
final double timeInS,
final Optional<Integer> nbParallelizedTransations) {
final Optional<Integer> nbParallelizedTransactions) {
final StringBuilder message = new StringBuilder();
final int nbTransactions = block.getBody().getTransactions().size();
message.append("Imported #%,d (%s)|%5d tx");
@@ -630,9 +635,9 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet
(block.getHeader().getGasUsed() * 100.0) / block.getHeader().getGasLimit(),
timeInS,
mgasPerSec));
if (nbParallelizedTransations.isPresent()) {
if (nbParallelizedTransactions.isPresent()) {
double parallelizedTxPercentage =
(double) (nbParallelizedTransations.get() * 100) / nbTransactions;
(double) (nbParallelizedTransactions.get() * 100) / nbTransactions;
message.append("| parallel txs %5.1f%%");
messageArgs.add(parallelizedTxPercentage);
}

View File

@@ -31,7 +31,7 @@ public class ForkSupportHelper {
"Configuration error, no schedule for " + hardforkId.name() + " fork set");
}
if (blockTimestamp < maybeForkMilestone.get()) {
if (Long.compareUnsigned(blockTimestamp, maybeForkMilestone.get()) < 0) {
return ValidationResult.invalid(
RpcErrorType.UNSUPPORTED_FORK,
hardforkId.name() + " configured to start at timestamp: " + maybeForkMilestone.get());

View File

@@ -148,7 +148,7 @@ public enum RpcErrorType implements RpcMethodError {
// Transaction validation failures
NONCE_TOO_LOW(-32001, "Nonce too low"),
INVALID_TRANSACTION_SIGNATURE(-32002, "Invalid signature"),
INVALID_TRANSACTION_TYPE(-32602, "Invalid transaction type"),
INVALID_TRANSACTION_TYPE(INVALID_PARAMS_ERROR_CODE, "Invalid transaction type"),
INTRINSIC_GAS_EXCEEDS_LIMIT(-32003, "Intrinsic gas exceeds gas limit"),
TRANSACTION_UPFRONT_COST_EXCEEDS_BALANCE(-32004, "Upfront cost exceeds account balance"),
EXCEEDS_BLOCK_GAS_LIMIT(-32005, "Transaction gas limit exceeds block gas limit"),

View File

@@ -130,8 +130,9 @@ public class TransactionCompleteResult implements TransactionResult {
this.yParity = Quantity.create(transaction.getYParity());
this.v =
(transactionType == TransactionType.ACCESS_LIST
|| transactionType == TransactionType.EIP1559)
|| transactionType == TransactionType.EIP1559
|| transactionType == TransactionType.DELEGATE_CODE
|| transactionType == TransactionType.BLOB)
? Quantity.create(transaction.getYParity())
: null;
}

View File

@@ -115,7 +115,9 @@ public class TransactionPendingResult implements TransactionResult {
this.yParity = Quantity.create(transaction.getYParity());
this.v =
(transactionType == TransactionType.ACCESS_LIST
|| transactionType == TransactionType.EIP1559)
|| transactionType == TransactionType.EIP1559
|| transactionType == TransactionType.DELEGATE_CODE
|| transactionType == TransactionType.BLOB)
? Quantity.create(transaction.getYParity())
: null;
}

View File

@@ -22,7 +22,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
@@ -33,6 +32,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.DebugTraceTran
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;
@@ -59,10 +60,7 @@ public class DebugTraceBlockByHashTest {
@Mock private BlockchainQueries blockchainQueries;
@Mock private ObservableMetricsSystem metricsSystem;
@Mock private Blockchain blockchain;
@Mock private Block block;
private DebugTraceBlockByHash debugTraceBlockByHash;
private final Hash blockHash =
Hash.fromHexString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
@BeforeEach
public void setUp() {
@@ -79,13 +77,18 @@ public class DebugTraceBlockByHashTest {
@SuppressWarnings("unchecked")
@Test
public void shouldReturnCorrectResponse() {
final Object[] params = new Object[] {blockHash};
final Block block =
new BlockDataGenerator()
.block(
BlockDataGenerator.BlockOptions.create()
.setBlockHeaderFunctions(new MainnetBlockHeaderFunctions()));
final Object[] params = new Object[] {block.getHash()};
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_traceBlockByHash", params));
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchain.getBlockByHash(blockHash)).thenReturn(Optional.of(block));
when(block.getHash()).thenReturn(blockHash);
when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block));
DebugTraceTransactionResult result1 = mock(DebugTraceTransactionResult.class);
DebugTraceTransactionResult result2 = mock(DebugTraceTransactionResult.class);
@@ -96,7 +99,10 @@ public class DebugTraceBlockByHashTest {
mockedTracer
.when(
() ->
Tracer.processTracing(eq(blockchainQueries), eq(blockHash), any(Function.class)))
Tracer.processTracing(
eq(blockchainQueries),
eq(Optional.of(block.getHeader())),
any(Function.class)))
.thenReturn(Optional.of(resultList));
final JsonRpcResponse jsonRpcResponse = debugTraceBlockByHash.response(request);

View File

@@ -154,7 +154,9 @@ public class DebugTraceBlockTest {
.when(
() ->
Tracer.processTracing(
eq(blockchainQueries), eq(block.getHash()), any(Function.class)))
eq(blockchainQueries),
eq(Optional.of(block.getHeader())),
any(Function.class)))
.thenReturn(Optional.of(resultList));
final JsonRpcResponse jsonRpcResponse = debugTraceBlock.response(request);

View File

@@ -124,6 +124,27 @@ public class EthCallTest {
assertThat(overrideMap).containsValue(override);
}
@Test
public void stateOverridesWithState() {
StateOverrideMap expectedOverrides = new StateOverrideMap();
StateOverride override =
new StateOverride.Builder().withState(Map.of("0x1234", "0x5678")).build();
final Address address = Address.fromHexString("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3");
expectedOverrides.put(address, override);
final JsonRpcRequestContext request =
ethCallRequestWithStateOverrides(callParameter(), "latest", expectedOverrides);
Optional<StateOverrideMap> maybeOverrideMap = method.getAddressStateOverrideMap(request);
assertThat(maybeOverrideMap.isPresent()).isTrue();
StateOverrideMap overrideMap = maybeOverrideMap.get();
assertThat(overrideMap.keySet()).hasSize(1);
assertThat(overrideMap.values()).hasSize(1);
assertThat(overrideMap).containsKey(address);
assertThat(overrideMap).containsValue(override);
}
@Test
public void fullStateOverrides() {
StateOverrideMap suppliedOverrides = new StateOverrideMap();
@@ -493,6 +514,7 @@ public class EthCallTest {
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(isAllowedExceedingBalance)
.isAllowFutureNonce(true)
.build();
verify(transactionSimulator)

View File

@@ -140,7 +140,7 @@ public class EthEstimateGasTest {
final JsonRpcRequestContext request =
ethEstimateGasRequest(defaultLegacyTransactionCallParameter(Wei.ZERO));
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO, Optional.empty())),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
@@ -193,11 +193,26 @@ public class EthEstimateGasTest {
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldUseNonceParameterWhenIsPresent() {
final Wei gasPrice = Wei.of(1000);
final long nonce = 0L;
final JsonRpcRequestContext request =
ethEstimateGasRequest(
eip1559TransactionCallParameter(Optional.of(gasPrice), Optional.of(nonce)));
getMockTransactionSimulatorResult(
true, 1L, gasPrice, Optional.empty(), latestBlockHeader, Optional.of(nonce));
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L));
assertThat(method.response(request)).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void shouldNotErrorWhenGasPricePresentForEip1559Transaction() {
final Wei gasPrice = Wei.of(1000);
final JsonRpcRequestContext request =
ethEstimateGasRequest(eip1559TransactionCallParameter(Optional.of(gasPrice)));
ethEstimateGasRequest(
eip1559TransactionCallParameter(Optional.of(gasPrice), Optional.empty()));
mockTransientProcessorResultGasEstimate(
1L, true, gasPrice, Optional.empty(), latestBlockHeader);
@@ -379,9 +394,11 @@ public class EthEstimateGasTest {
verify(transactionSimulator)
.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO, Optional.empty())),
eq(Optional.empty()), // no account overrides
eq(TransactionValidationParams.transactionSimulatorAllowExceedingBalance()),
eq(
TransactionValidationParams
.transactionSimulatorAllowExceedingBalanceAndFutureNonceParams),
any(OperationTracer.class),
eq(latestBlockHeader));
}
@@ -396,9 +413,9 @@ public class EthEstimateGasTest {
verify(transactionSimulator)
.process(
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO)),
eq(modifiedLegacyTransactionCallParameter(Wei.ZERO, Optional.empty())),
eq(Optional.empty()), // no account overrides
eq(TransactionValidationParams.transactionSimulator()),
eq(TransactionValidationParams.transactionSimulatorAllowFutureNonce()),
any(OperationTracer.class),
eq(latestBlockHeader));
}
@@ -456,7 +473,8 @@ public class EthEstimateGasTest {
final String validationFailedErrorMessage,
final BlockHeader blockHeader) {
final TransactionSimulatorResult mockTxSimResult =
getMockTransactionSimulatorResult(false, 0, Wei.ZERO, Optional.empty(), blockHeader);
getMockTransactionSimulatorResult(
false, 0, Wei.ZERO, Optional.empty(), blockHeader, Optional.empty());
when(mockTxSimResult.getValidationResult())
.thenReturn(
validationFailedErrorMessage == null
@@ -493,7 +511,7 @@ public class EthEstimateGasTest {
final Optional<Bytes> revertReason,
final BlockHeader blockHeader) {
getMockTransactionSimulatorResult(
isSuccessful, estimateGas, gasPrice, revertReason, blockHeader);
isSuccessful, estimateGas, gasPrice, revertReason, blockHeader, Optional.empty());
}
@SuppressWarnings("ReferenceEquality")
@@ -502,11 +520,12 @@ public class EthEstimateGasTest {
final long estimateGas,
final Wei gasPrice,
final Optional<Bytes> revertReason,
final BlockHeader blockHeader) {
final BlockHeader blockHeader,
final Optional<Long> maybeNonce) {
final TransactionSimulatorResult mockTxSimResult = mock(TransactionSimulatorResult.class);
if (blockHeader == pendingBlockHeader) {
when(transactionSimulator.processOnPending(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(modifiedLegacyTransactionCallParameter(gasPrice, maybeNonce)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
@@ -521,7 +540,7 @@ public class EthEstimateGasTest {
.thenReturn(Optional.of(mockTxSimResult));
} else {
when(transactionSimulator.process(
eq(modifiedLegacyTransactionCallParameter(gasPrice)),
eq(modifiedLegacyTransactionCallParameter(gasPrice, maybeNonce)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
@@ -536,7 +555,7 @@ public class EthEstimateGasTest {
.thenReturn(Optional.of(mockTxSimResult));
// for testing different combination of gasPrice params
when(transactionSimulator.process(
eq(modifiedEip1559TransactionCallParameter(Optional.of(gasPrice))),
eq(modifiedEip1559TransactionCallParameter(Optional.of(gasPrice), maybeNonce)),
eq(Optional.empty()), // no account overrides
any(TransactionValidationParams.class),
any(OperationTracer.class),
@@ -569,7 +588,8 @@ public class EthEstimateGasTest {
.build();
}
private CallParameter modifiedLegacyTransactionCallParameter(final Wei gasPrice) {
private CallParameter modifiedLegacyTransactionCallParameter(
final Wei gasPrice, final Optional<Long> maybeNonce) {
return new CallParameter(
Address.fromHexString("0x0"),
Address.fromHexString("0x0"),
@@ -580,14 +600,15 @@ public class EthEstimateGasTest {
Wei.ZERO,
Bytes.EMPTY,
Optional.empty(),
Optional.empty());
maybeNonce);
}
private CallParameter eip1559TransactionCallParameter() {
return eip1559TransactionCallParameter(Optional.empty());
return eip1559TransactionCallParameter(Optional.empty(), Optional.empty());
}
private JsonCallParameter eip1559TransactionCallParameter(final Optional<Wei> maybeGasPrice) {
private JsonCallParameter eip1559TransactionCallParameter(
final Optional<Wei> maybeGasPrice, final Optional<Long> maybeNonce) {
return new JsonCallParameter.JsonCallParameterBuilder()
.withFrom(Address.fromHexString("0x0"))
.withTo(Address.fromHexString("0x0"))
@@ -597,14 +618,16 @@ public class EthEstimateGasTest {
.withValue(Wei.ZERO)
.withInput(Bytes.EMPTY)
.withStrict(false)
.withNonce(maybeNonce.map(UnsignedLongParameter::new).orElse(null))
.build();
}
private CallParameter modifiedEip1559TransactionCallParameter() {
return modifiedEip1559TransactionCallParameter(Optional.empty());
return modifiedEip1559TransactionCallParameter(Optional.empty(), Optional.empty());
}
private CallParameter modifiedEip1559TransactionCallParameter(final Optional<Wei> gasPrice) {
private CallParameter modifiedEip1559TransactionCallParameter(
final Optional<Wei> gasPrice, final Optional<Long> maybeNonce) {
return new CallParameter(
Address.fromHexString("0x0"),
Address.fromHexString("0x0"),
@@ -615,7 +638,7 @@ public class EthEstimateGasTest {
Wei.ZERO,
Bytes.EMPTY,
Optional.empty(),
Optional.empty());
maybeNonce);
}
private JsonRpcRequestContext ethEstimateGasRequest(final CallParameter callParameter) {

View File

@@ -0,0 +1,62 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.datatypes.HardforkId.MainnetHardforkId.PRAGUE;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.ForkSupportHelper.validateForkSupported;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import java.util.Optional;
import org.junit.jupiter.api.Test;
class ForkSupportHelperTest {
@Test
void validForkIfMilestoneOlderThanBlock() {
assertThat(validateForkSupported(PRAGUE, Optional.of(0L), 1))
.isEqualTo(ValidationResult.valid());
}
@Test
void validForkIfMilestoneEqualToBlock() {
assertThat(validateForkSupported(PRAGUE, Optional.of(0L), 0))
.isEqualTo(ValidationResult.valid());
}
@Test
void validForkWhenTimestampOverflowsSignedLong() {
long unsignedLongMaxValue = Long.parseUnsignedLong("18446744073709551615");
assertThat(validateForkSupported(PRAGUE, Optional.of(1L), unsignedLongMaxValue))
.isEqualTo(ValidationResult.valid());
}
@Test
void unsupportedForkIfMilestoneMisconfigured() {
assertThat(validateForkSupported(PRAGUE, Optional.empty(), 0))
.isEqualTo(
ValidationResult.invalid(RpcErrorType.UNSUPPORTED_FORK, "message equality ignored"));
}
@Test
void unsupportedForkIfBlockOlderThanMilestone() {
assertThat(validateForkSupported(PRAGUE, Optional.of(1L), 0))
.isEqualTo(
ValidationResult.invalid(RpcErrorType.UNSUPPORTED_FORK, "message equality ignored"));
}
}

View File

@@ -4,7 +4,7 @@
"method": "debug_traceCall",
"params": [
{
"to": "0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC",
"to": "0x0000f90827f1c53a10cb7a02335b175320002935",
"data": "0x000000000000000000000000000000000000000000000000000000000001A00E"
},
"latest",

View File

@@ -0,0 +1,192 @@
{
"request" : {
"jsonrpc" : "2.0",
"method" : "debug_traceCall",
"params" : [ {
"from" : "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73",
"to" : "0x0050000000000000000000000000000000000000",
"gas" : "0xfffff2",
"gasPrice" : "0xef",
"value" : "0x0",
"data" : "0x0000000000000000000000000030000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001",
"nonce" : "0x1F"
}, "latest",
{
"disableMemory": true, "disableStack": true, "disableStorage": true
} ],
"id" : 1
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": {
"gas" : 22070,
"failed" : false,
"returnValue" : "f000000000000000000000000000000000000000000000000000000000000002",
"structLogs" : [ {
"pc" : 0,
"op" : "PUSH1",
"gas" : 16755910,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 2,
"op" : "PUSH1",
"gas" : 16755907,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 4,
"op" : "PUSH1",
"gas" : 16755904,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 6,
"op" : "CALLDATASIZE",
"gas" : 16755901,
"gasCost" : 2,
"depth" : 1
}, {
"pc" : 7,
"op" : "SUB",
"gas" : 16755899,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 8,
"op" : "DUP1",
"gas" : 16755896,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 9,
"op" : "PUSH1",
"gas" : 16755893,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 11,
"op" : "PUSH1",
"gas" : 16755890,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 13,
"op" : "CALLDATACOPY",
"gas" : 16755887,
"gasCost" : 9,
"depth" : 1
}, {
"pc" : 14,
"op" : "PUSH1",
"gas" : 16755878,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 16,
"op" : "CALLVALUE",
"gas" : 16755875,
"gasCost" : 2,
"depth" : 1
}, {
"pc" : 17,
"op" : "PUSH1",
"gas" : 16755873,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 19,
"op" : "CALLDATALOAD",
"gas" : 16755870,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 20,
"op" : "GAS",
"gas" : 16755867,
"gasCost" : 2,
"depth" : 1
}, {
"pc" : 21,
"op" : "CALLCODE",
"gas" : 16755865,
"gasCost" : 16494066,
"depth" : 1
}, {
"pc" : 0,
"op" : "PUSH1",
"gas" : 16493366,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 2,
"op" : "CALLDATALOAD",
"gas" : 16493363,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 3,
"op" : "PUSH1",
"gas" : 16493360,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 5,
"op" : "ADD",
"gas" : 16493357,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 6,
"op" : "PUSH1",
"gas" : 16493354,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 8,
"op" : "MSTORE",
"gas" : 16493351,
"gasCost" : 6,
"depth" : 2
}, {
"pc" : 9,
"op" : "PUSH1",
"gas" : 16493345,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 11,
"op" : "PUSH1",
"gas" : 16493342,
"gasCost" : 3,
"depth" : 2
}, {
"pc" : 13,
"op" : "RETURN",
"gas" : 16493339,
"gasCost" : 0,
"depth" : 2
}, {
"pc" : 22,
"op" : "PUSH1",
"gas" : 16755138,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 24,
"op" : "PUSH1",
"gas" : 16755135,
"gasCost" : 3,
"depth" : 1
}, {
"pc" : 26,
"op" : "RETURN",
"gas" : 16755132,
"gasCost" : 0,
"depth" : 1
} ]
}
},
"statusCode": 200
}

View File

@@ -0,0 +1,21 @@
{
"request": {
"id": 4,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x608060405234801561001057600080fd5b50610157806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bdab8bf146100515780639ae97baa14610068575b600080fd5b34801561005d57600080fd5b5061006661007f565b005b34801561007457600080fd5b5061007d6100b9565b005b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60016040518082815260200191505060405180910390a1565b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60026040518082815260200191505060405180910390a17fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60036040518082815260200191505060405180910390a15600a165627a7a7230582010ddaa52e73a98c06dbcd22b234b97206c1d7ed64a7c048e10c2043a3d2309cb0029",
"nonce": "0x2f"
},
"latest"
]
},
"response": {
"jsonrpc": "2.0",
"id": 4,
"result": "0x60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bdab8bf146100515780639ae97baa14610068575b600080fd5b34801561005d57600080fd5b5061006661007f565b005b34801561007457600080fd5b5061007d6100b9565b005b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60016040518082815260200191505060405180910390a1565b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60026040518082815260200191505060405180910390a17fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60036040518082815260200191505060405180910390a15600a165627a7a7230582010ddaa52e73a98c06dbcd22b234b97206c1d7ed64a7c048e10c2043a3d2309cb0029"
},
"statusCode": 200
}

View File

@@ -0,0 +1,21 @@
{
"request": {
"id": 4,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x608060405234801561001057600080fd5b50610157806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bdab8bf146100515780639ae97baa14610068575b600080fd5b34801561005d57600080fd5b5061006661007f565b005b34801561007457600080fd5b5061007d6100b9565b005b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60016040518082815260200191505060405180910390a1565b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60026040518082815260200191505060405180910390a17fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60036040518082815260200191505060405180910390a15600a165627a7a7230582010ddaa52e73a98c06dbcd22b234b97206c1d7ed64a7c048e10c2043a3d2309cb0029",
"nonce": "0x22"
},
"latest"
]
},
"response": {
"jsonrpc": "2.0",
"id": 4,
"result": "0x60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bdab8bf146100515780639ae97baa14610068575b600080fd5b34801561005d57600080fd5b5061006661007f565b005b34801561007457600080fd5b5061007d6100b9565b005b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60016040518082815260200191505060405180910390a1565b7fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60026040518082815260200191505060405180910390a17fa53887c1eed04528e23301f55ad49a91634ef5021aa83a97d07fd16ed71c039a60036040518082815260200191505060405180910390a15600a165627a7a7230582010ddaa52e73a98c06dbcd22b234b97206c1d7ed64a7c048e10c2043a3d2309cb0029"
},
"statusCode": 200
}

View File

@@ -0,0 +1,21 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"to": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"value": "0x1",
"nonce": "0x29"
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x5208"
},
"statusCode": 200
}

View File

@@ -0,0 +1,21 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"to": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"value": "0x1",
"nonce": "0x22"
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x5208"
},
"statusCode": 200
}

View File

@@ -0,0 +1,24 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"to": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"value": "0x1",
"nonce": "0x2"
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32001,
"message": "Nonce too low (transaction nonce 2 below sender account nonce 34)"
}
},
"statusCode": 200
}

View File

@@ -52,6 +52,7 @@
"type": "0x3",
"value": "0x0",
"yParity": "0x0",
"v" : "0x0",
"r": "0x6ae0612cfda43a9b464b10b4881c6fc2e4c24533cf89bbe07934da65c3ae49ce",
"s": "0x125387aeb222ec51130cf99cbdabf24bd4a881914faed69f254e4a3f4bc507fc",
"blobVersionedHashes": [

View File

@@ -30,6 +30,7 @@
"type": "0x3",
"value": "0x0",
"yParity": "0x0",
"v" : "0x0",
"r": "0x6ae0612cfda43a9b464b10b4881c6fc2e4c24533cf89bbe07934da65c3ae49ce",
"s": "0x125387aeb222ec51130cf99cbdabf24bd4a881914faed69f254e4a3f4bc507fc",
"blobVersionedHashes": [

View File

@@ -30,6 +30,7 @@
"type": "0x3",
"value": "0x0",
"yParity": "0x0",
"v" : "0x0",
"r": "0x6ae0612cfda43a9b464b10b4881c6fc2e4c24533cf89bbe07934da65c3ae49ce",
"s": "0x125387aeb222ec51130cf99cbdabf24bd4a881914faed69f254e4a3f4bc507fc",
"blobVersionedHashes": [

View File

@@ -30,6 +30,7 @@ import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.BlockSi
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.MinPriorityFeePerGasTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.PriceTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.ProcessingResultTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.SkipSenderTransactionSelector;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
@@ -151,6 +152,7 @@ public class BlockTransactionSelector {
private List<AbstractTransactionSelector> createTransactionSelectors(
final BlockSelectionContext context) {
return List.of(
new SkipSenderTransactionSelector(context),
new BlockSizeTransactionSelector(context),
new BlobSizeTransactionSelector(context),
new PriceTransactionSelector(context),
@@ -442,7 +444,8 @@ public class BlockTransactionSelector {
evaluationContext, BLOCK_SELECTION_TIMEOUT, txWorldStateUpdater);
}
pluginTransactionSelector.onTransactionSelected(evaluationContext, processingResult);
notifySelected(evaluationContext, processingResult);
blockWorldStateUpdater = worldState.updater();
LOG.atTrace()
.setMessage("Selected {} for block creation, evaluated in {}")
@@ -475,7 +478,7 @@ public class BlockTransactionSelector {
: selectionResult;
transactionSelectionResults.updateNotSelected(evaluationContext.getTransaction(), actualResult);
pluginTransactionSelector.onTransactionNotSelected(evaluationContext, actualResult);
notifyNotSelected(evaluationContext, actualResult);
LOG.atTrace()
.setMessage(
"Not selected {} for block creation with result {} (original result {}), evaluated in {}")
@@ -550,6 +553,26 @@ public class BlockTransactionSelector {
return handleTransactionNotSelected(evaluationContext, selectionResult);
}
private void notifySelected(
final TransactionEvaluationContext evaluationContext,
final TransactionProcessingResult processingResult) {
for (var selector : transactionSelectors) {
selector.onTransactionSelected(evaluationContext, processingResult);
}
pluginTransactionSelector.onTransactionSelected(evaluationContext, processingResult);
}
private void notifyNotSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult selectionResult) {
for (var selector : transactionSelectors) {
selector.onTransactionNotSelected(evaluationContext, selectionResult);
}
pluginTransactionSelector.onTransactionNotSelected(evaluationContext, selectionResult);
}
private void checkCancellation() {
if (isCancelled.get()) {
throw new CancellationException("Cancelled during transaction selection.");

View File

@@ -25,7 +25,7 @@ import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
* transactions.
*/
public abstract class AbstractTransactionSelector {
final BlockSelectionContext context;
protected final BlockSelectionContext context;
public AbstractTransactionSelector(final BlockSelectionContext context) {
this.context = context;
@@ -55,4 +55,24 @@ public abstract class AbstractTransactionSelector {
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult);
/**
* Method called when a transaction is selected to be added to a block.
*
* @param evaluationContext The current selection context
* @param processingResult The result of processing the selected transaction.
*/
public void onTransactionSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionProcessingResult processingResult) {}
/**
* Method called when a transaction is not selected to be added to a block.
*
* @param evaluationContext The current selection context
* @param transactionSelectionResult The transaction selection result
*/
public void onTransactionNotSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult transactionSelectionResult) {}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SkipSenderTransactionSelector extends AbstractTransactionSelector {
private static final Logger LOG = LoggerFactory.getLogger(SkipSenderTransactionSelector.class);
private final Set<Address> skippedSenders = new HashSet<>();
public SkipSenderTransactionSelector(final BlockSelectionContext context) {
super(context);
}
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults ignored) {
final var sender = evaluationContext.getTransaction().getSender();
if (skippedSenders.contains(sender)) {
LOG.atTrace()
.setMessage("Not selecting tx {} since its sender {} is in the skip list")
.addArgument(() -> evaluationContext.getPendingTransaction().toTraceLog())
.addArgument(sender)
.log();
return TransactionSelectionResult.SENDER_WITH_PREVIOUS_TX_NOT_SELECTED;
}
return TransactionSelectionResult.SELECTED;
}
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
return TransactionSelectionResult.SELECTED;
}
/**
* When a transaction is not selected we can skip processing any following transaction from the
* same sender, since it will never be selected due to the nonce gap, so we add the sender to the
* skip list.
*
* @param evaluationContext The current selection context
* @param transactionSelectionResult The transaction selection result
*/
@Override
public void onTransactionNotSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult transactionSelectionResult) {
final var sender = evaluationContext.getTransaction().getSender();
skippedSenders.add(sender);
LOG.trace("Sender {} added to the skip list", sender);
}
}

View File

@@ -18,6 +18,11 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.awaitility.Awaitility.await;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER1;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER2;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER3;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER4;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER5;
import static org.hyperledger.besu.ethereum.core.MiningConfiguration.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXECUTION_INTERRUPTED;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_LOW;
@@ -62,7 +67,6 @@ 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.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
@@ -80,7 +84,6 @@ import org.hyperledger.besu.ethereum.trie.diffbased.common.provider.WorldStateQu
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@@ -127,10 +130,6 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected static final double MIN_OCCUPANCY_80_PERCENT = 0.8;
protected static final double MIN_OCCUPANCY_100_PERCENT = 1;
protected static final BigInteger CHAIN_ID = BigInteger.valueOf(42L);
protected static final KeyPair keyPair =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
protected static final Address sender =
Address.extract(Hash.hash(keyPair.getPublicKey().getEncodedBytes()));
protected final MetricsSystem metricsSystem = new NoOpMetricsSystem();
protected GenesisConfig genesisConfig;
@@ -180,7 +179,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
worldState = InMemoryKeyValueStorageProvider.createInMemoryWorldState();
final var worldStateUpdater = worldState.updater();
worldStateUpdater.createAccount(sender, 0, Wei.of(1_000_000_000L));
Arrays.stream(Sender.values())
.map(Sender::address)
.forEach(address -> worldStateUpdater.createAccount(address, 0, Wei.of(1_000_000_000L)));
worldStateUpdater.commit();
when(protocolContext.getWorldStateArchive().getWorldState(any(WorldStateQueryParams.class)))
@@ -303,9 +304,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
Wei.ZERO,
transactionSelectionService);
final List<Transaction> transactionsToInject = Lists.newArrayList();
final List<Transaction> transactionsToInject = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
final Transaction tx = createTransaction(i, Wei.of(7), 100_000, Sender.values()[i]);
transactionsToInject.add(tx);
if (i == 1) {
ensureTransactionIsInvalid(tx, TransactionInvalidReason.NONCE_TOO_LOW);
@@ -389,9 +390,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
// NOTE - PendingTransactions outputs these in nonce order
final Transaction[] txs =
new Transaction[] {
createTransaction(1, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.79)),
createTransaction(2, Wei.of(10), blockHeader.getGasLimit()),
createTransaction(3, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.1))
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.79), SENDER1),
createTransaction(0, Wei.of(10), blockHeader.getGasLimit(), SENDER2),
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.1), SENDER3)
};
for (final Transaction tx : txs) {
@@ -425,10 +426,10 @@ public abstract class AbstractBlockTransactionSelectorTest {
// NOTE - PendingTransactions will output these in nonce order.
final Transaction[] txs =
new Transaction[] {
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.15)),
createTransaction(1, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.79)),
createTransaction(2, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.25)),
createTransaction(3, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.1))
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.15), SENDER1),
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.79), SENDER2),
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.25), SENDER3),
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.1), SENDER4)
};
for (Transaction tx : txs) {
@@ -443,6 +444,44 @@ public abstract class AbstractBlockTransactionSelectorTest {
.containsOnly(entry(txs[2], TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD));
}
@Test
public void ifATransactionIsNotSelectedFollowingOnesFromTheSameSenderAreSkipped() {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final BlockTransactionSelector selector =
createBlockSelectorAndSetupTxPool(
defaultTestMiningConfiguration,
transactionProcessor,
blockHeader,
miningBeneficiary,
Wei.ZERO,
transactionSelectionService);
// Add 3 transactions from the same sender to the Pending Transactions
// first is selected
// second id not selected
// third is skipped, not processed, since cannot be selected due to the nonce gap
final Transaction[] txs =
new Transaction[] {
createTransaction(0, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.5), SENDER1),
createTransaction(1, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.6), SENDER1),
createTransaction(2, Wei.of(10), (long) (blockHeader.getGasLimit() * 0.1), SENDER1)
};
for (Transaction tx : txs) {
ensureTransactionIsValid(tx);
}
transactionPool.addRemoteTransactions(Arrays.stream(txs).toList());
final TransactionSelectionResults results = selector.buildTransactionListForBlock();
assertThat(results.getSelectedTransactions()).containsExactly(txs[0]);
assertThat(results.getNotSelectedTransactions())
.containsOnly(
entry(txs[1], TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_GAS),
entry(txs[2], TransactionSelectionResult.SENDER_WITH_PREVIOUS_TX_NOT_SELECTED));
}
@Test
public void transactionSelectionStopsWhenBlockIsFull() {
final ProcessableBlockHeader blockHeader = createBlock(3_000_000);
@@ -471,18 +510,18 @@ public abstract class AbstractBlockTransactionSelectorTest {
// 4) min gas cost (not selected since selection stopped after tx 3)
// NOTE - PendingTransactions outputs these in nonce order
final long gasLimit0 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit1 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit2 = blockHeader.getGasLimit() - gasLimit0 - minTxGasCost;
final long gasLimit3 = minTxGasCost;
final long gasLimit4 = minTxGasCost;
final long gasLimit0s1 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit1s1 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit0s2 = blockHeader.getGasLimit() - gasLimit0s1 - minTxGasCost;
final long gasLimit1s2 = minTxGasCost;
final long gasLimit2s2 = minTxGasCost;
final List<Transaction> transactionsToInject = Lists.newArrayList();
transactionsToInject.add(createTransaction(0, Wei.of(7), gasLimit0));
transactionsToInject.add(createTransaction(1, Wei.of(7), gasLimit1));
transactionsToInject.add(createTransaction(2, Wei.of(7), gasLimit2));
transactionsToInject.add(createTransaction(3, Wei.of(7), gasLimit3));
transactionsToInject.add(createTransaction(4, Wei.of(7), gasLimit4));
transactionsToInject.add(createTransaction(0, Wei.of(7), gasLimit0s1, SENDER1));
transactionsToInject.add(createTransaction(1, Wei.of(7), gasLimit1s1, SENDER1));
transactionsToInject.add(createTransaction(0, Wei.of(7), gasLimit0s2, SENDER2));
transactionsToInject.add(createTransaction(1, Wei.of(7), gasLimit1s2, SENDER2));
transactionsToInject.add(createTransaction(2, Wei.of(7), gasLimit2s2, SENDER2));
for (final Transaction tx : transactionsToInject) {
ensureTransactionIsValid(tx);
@@ -532,16 +571,16 @@ public abstract class AbstractBlockTransactionSelectorTest {
// 3) min gas cost (skipped since not enough gas remaining)
// NOTE - PendingTransactions outputs these in nonce order
final long gasLimit0 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit1 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit2 = blockHeader.getGasLimit() - gasLimit0 - (minTxGasCost - 1);
final long gasLimit3 = minTxGasCost;
final long gasLimit0s1 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit1s1 = (long) (blockHeader.getGasLimit() * 0.9);
final long gasLimit0s2 = blockHeader.getGasLimit() - gasLimit0s1 - (minTxGasCost - 1);
final long gasLimit1s2 = minTxGasCost;
final List<Transaction> transactionsToInject = Lists.newArrayList();
transactionsToInject.add(createTransaction(0, Wei.of(10), gasLimit0));
transactionsToInject.add(createTransaction(1, Wei.of(10), gasLimit1));
transactionsToInject.add(createTransaction(2, Wei.of(10), gasLimit2));
transactionsToInject.add(createTransaction(3, Wei.of(10), gasLimit3));
final List<Transaction> transactionsToInject = new ArrayList<>(4);
transactionsToInject.add(createTransaction(0, Wei.of(10), gasLimit0s1, SENDER1));
transactionsToInject.add(createTransaction(1, Wei.of(10), gasLimit1s1, SENDER1));
transactionsToInject.add(createTransaction(0, Wei.of(10), gasLimit0s2, SENDER2));
transactionsToInject.add(createTransaction(1, Wei.of(10), gasLimit1s2, SENDER2));
for (final Transaction tx : transactionsToInject) {
ensureTransactionIsValid(tx);
@@ -600,13 +639,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final ProcessableBlockHeader blockHeader = createBlock(300_000);
final Address miningBeneficiary = AddressHelpers.ofValue(1);
final Transaction selected = createTransaction(0, Wei.of(10), 21_000);
final Transaction selected = createTransaction(0, Wei.of(10), 21_000, SENDER1);
ensureTransactionIsValid(selected, 21_000, 0);
final Transaction notSelectedTransient = createTransaction(1, Wei.of(10), 21_000);
final Transaction notSelectedTransient = createTransaction(1, Wei.of(10), 21_000, SENDER1);
ensureTransactionIsValid(notSelectedTransient, 21_000, 0);
final Transaction notSelectedInvalid = createTransaction(2, Wei.of(10), 21_000);
final Transaction notSelectedInvalid = createTransaction(2, Wei.of(10), 21_000, SENDER2);
ensureTransactionIsValid(notSelectedInvalid, 21_000, 0);
final PluginTransactionSelectorFactory transactionSelectorFactory =
@@ -723,12 +762,12 @@ public abstract class AbstractBlockTransactionSelectorTest {
Wei.ZERO,
transactionSelectionService);
transactionPool.addRemoteTransactions(List.of(selected, notSelected, selected3));
transactionPool.addRemoteTransactions(List.of(selected, notSelected));
final TransactionSelectionResults transactionSelectionResults =
selector.buildTransactionListForBlock();
assertThat(transactionSelectionResults.getSelectedTransactions()).contains(selected, selected3);
assertThat(transactionSelectionResults.getSelectedTransactions()).contains(selected);
assertThat(transactionSelectionResults.getNotSelectedTransactions())
.containsOnly(
entry(notSelected, PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID_TRANSIENT));
@@ -1170,7 +1209,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
final List<Transaction> transactionsToInject = new ArrayList<>(txCount);
for (int i = 0; i < txCount - 1; i++) {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
final Transaction tx = createTransaction(0, Wei.of(7), 100_000, Sender.values()[i]);
transactionsToInject.add(tx);
if (processingTooLate) {
ensureTransactionIsInvalid(tx, txInvalidReason, fastProcessingTxTime);
@@ -1179,7 +1218,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
}
}
final Transaction lateTx = createTransaction(2, Wei.of(7), 100_000);
final Transaction lateTx = createTransaction(0, Wei.of(7), 100_000, SENDER5);
transactionsToInject.add(lateTx);
if (processingTooLate) {
ensureTransactionIsInvalid(lateTx, txInvalidReason, longProcessingTxTime);
@@ -1293,7 +1332,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
worldState,
transactionPool,
blockHeader,
this::createReceipt,
protocolSchedule.getByBlockHeader(blockHeader).getTransactionReceiptFactory(),
this::isCancelled,
miningBeneficiary,
blobGasPrice,
@@ -1317,6 +1356,11 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected Transaction createTransaction(
final int nonce, final Wei gasPrice, final long gasLimit) {
return createTransaction(nonce, gasPrice, gasLimit, SENDER1);
}
protected Transaction createTransaction(
final int nonce, final Wei gasPrice, final long gasLimit, final Sender sender) {
return Transaction.builder()
.gasLimit(gasLimit)
.gasPrice(gasPrice)
@@ -1324,10 +1368,10 @@ public abstract class AbstractBlockTransactionSelectorTest {
.payload(Bytes.EMPTY)
.to(Address.ID)
.value(Wei.of(nonce))
.sender(sender)
.sender(sender.address())
.chainId(CHAIN_ID)
.guessType()
.signAndBuild(keyPair);
.signAndBuild(sender.keyPair());
}
protected Transaction createEIP1559Transaction(
@@ -1335,6 +1379,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Wei maxFeePerGas,
final Wei maxPriorityFeePerGas,
final long gasLimit) {
return createEIP1559Transaction(nonce, maxFeePerGas, maxPriorityFeePerGas, gasLimit, SENDER1);
}
protected Transaction createEIP1559Transaction(
final int nonce,
final Wei maxFeePerGas,
final Wei maxPriorityFeePerGas,
final long gasLimit,
final Sender sender) {
return Transaction.builder()
.type(TransactionType.EIP1559)
.gasLimit(gasLimit)
@@ -1344,19 +1397,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
.payload(Bytes.EMPTY)
.to(Address.ID)
.value(Wei.of(nonce))
.sender(sender)
.sender(sender.address())
.chainId(CHAIN_ID)
.signAndBuild(keyPair);
}
// This is a duplicate of the MainnetProtocolSpec::frontierTransactionReceiptFactory
private TransactionReceipt createReceipt(
final TransactionType __,
final TransactionProcessingResult result,
final WorldState worldState,
final long gasUsed) {
return new TransactionReceipt(
worldState.rootHash(), gasUsed, Lists.newArrayList(), Optional.empty());
.signAndBuild(sender.keyPair());
}
protected void ensureTransactionIsValid(final Transaction tx) {
@@ -1507,4 +1550,34 @@ public abstract class AbstractBlockTransactionSelectorTest {
return new PluginTransactionSelectionResult(PluginStatus.PLUGIN_INVALID, invalidReason);
}
}
protected enum Sender {
// it is important to keep the addresses of the senders sorted, to make the tests reproducible,
// since a different sender address can change the order in which txs are selected,
// if all the other sorting fields are equal
SENDER1(4), // 0x1eff47bc3a10a45d4b230b5d10e37751fe6aa718
SENDER2(2), // 0x2b5ad5c4795c026514f8317c7a215e218dccd6cf
SENDER3(3), // 0x6813eb9362372eef6200f3b1dbc3f819671cba69
SENDER4(1), // 0x7e5f4552091a69125d5dfcb7b8c2659029395bdf
SENDER5(5); // 0xe1ab8145f7e55dc933d51a18c793f901a3a0b276
private final KeyPair keyPair;
private final Address address;
Sender(final int seed) {
final var privateKey =
SignatureAlgorithmFactory.getInstance().createPrivateKey(BigInteger.valueOf(seed));
final var publicKey = SignatureAlgorithmFactory.getInstance().createPublicKey(privateKey);
this.keyPair = new KeyPair(privateKey, publicKey);
this.address = Address.extract(Hash.hash(publicKey.getEncodedBytes()));
}
public KeyPair keyPair() {
return keyPair;
}
public Address address() {
return address;
}
}
}

View File

@@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.blockcreation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER1;
import static org.hyperledger.besu.ethereum.blockcreation.AbstractBlockTransactionSelectorTest.Sender.SENDER2;
import static org.hyperledger.besu.ethereum.core.MiningConfiguration.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.mockito.Mockito.mock;
@@ -246,19 +248,23 @@ public class LondonFeeMarketBlockTransactionSelectorTest
ImmutableMiningConfiguration.builder().from(defaultTestMiningConfiguration).build();
miningConfiguration.setMinPriorityFeePerGas(Wei.of(7));
final Transaction txSelected1 = createEIP1559Transaction(1, Wei.of(8), Wei.of(8), 100_000);
final Transaction txSelected1 =
createEIP1559Transaction(0, Wei.of(8), Wei.of(8), 100_000, SENDER1);
ensureTransactionIsValid(txSelected1);
// transaction txNotSelected1 should not be selected
final Transaction txNotSelected1 = createEIP1559Transaction(2, Wei.of(7), Wei.of(7), 100_000);
final Transaction txNotSelected1 =
createEIP1559Transaction(1, Wei.of(7), Wei.of(7), 100_000, SENDER1);
ensureTransactionIsValid(txNotSelected1);
// transaction txSelected2 should be selected
final Transaction txSelected2 = createEIP1559Transaction(3, Wei.of(8), Wei.of(8), 100_000);
final Transaction txSelected2 =
createEIP1559Transaction(0, Wei.of(8), Wei.of(8), 100_000, SENDER2);
ensureTransactionIsValid(txSelected2);
// transaction txNotSelected2 should not be selected
final Transaction txNotSelected2 = createEIP1559Transaction(4, Wei.of(8), Wei.of(6), 100_000);
final Transaction txNotSelected2 =
createEIP1559Transaction(1, Wei.of(8), Wei.of(6), 100_000, SENDER2);
ensureTransactionIsValid(txNotSelected2);
final BlockTransactionSelector selector =

View File

@@ -26,7 +26,7 @@ public class BlockProcessingResult extends BlockValidationResult {
private final Optional<BlockProcessingOutputs> yield;
private final boolean isPartial;
private Optional<Integer> nbParallelizedTransations = Optional.empty();
private Optional<Integer> nbParallelizedTransactions = Optional.empty();
/** A result indicating that processing failed. */
public static final BlockProcessingResult FAILED = new BlockProcessingResult("processing failed");
@@ -45,15 +45,15 @@ public class BlockProcessingResult extends BlockValidationResult {
* 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
* @param nbParallelizedTransactions potential number of parallelized transactions during block
* processing
*/
public BlockProcessingResult(
final Optional<BlockProcessingOutputs> yield,
final Optional<Integer> nbParallelizedTransations) {
final Optional<Integer> nbParallelizedTransactions) {
this.yield = yield;
this.isPartial = false;
this.nbParallelizedTransations = nbParallelizedTransations;
this.nbParallelizedTransactions = nbParallelizedTransactions;
}
/**
@@ -166,7 +166,7 @@ public class BlockProcessingResult extends BlockValidationResult {
*
* @return Optional of parallelized transactions during the block execution
*/
public Optional<Integer> getNbParallelizedTransations() {
return nbParallelizedTransations;
public Optional<Integer> getNbParallelizedTransactions() {
return nbParallelizedTransactions;
}
}

View File

@@ -191,7 +191,7 @@ public class MainnetBlockValidator implements BlockValidator {
return new BlockProcessingResult(
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)),
result.getNbParallelizedTransations());
result.getNbParallelizedTransactions());
}
} catch (MerkleTrieException ex) {
context.getWorldStateArchive().heal(ex.getMaybeAddress(), ex.getLocation());

View File

@@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
@@ -43,6 +44,8 @@ public class BlockHeader extends SealableBlockHeader
private final Supplier<ParsedExtraData> parsedExtraData;
private final Optional<Bytes> rawRlp;
public BlockHeader(
final Hash parentHash,
final Hash ommersHash,
@@ -66,6 +69,56 @@ public class BlockHeader extends SealableBlockHeader
final Bytes32 parentBeaconBlockRoot,
final Hash requestsHash,
final BlockHeaderFunctions blockHeaderFunctions) {
this(
parentHash,
ommersHash,
coinbase,
stateRoot,
transactionsRoot,
receiptsRoot,
logsBloom,
difficulty,
number,
gasLimit,
gasUsed,
timestamp,
extraData,
baseFee,
mixHashOrPrevRandao,
nonce,
withdrawalsRoot,
blobGasUsed,
excessBlobGas,
parentBeaconBlockRoot,
requestsHash,
blockHeaderFunctions,
Optional.empty());
}
private BlockHeader(
final Hash parentHash,
final Hash ommersHash,
final Address coinbase,
final Hash stateRoot,
final Hash transactionsRoot,
final Hash receiptsRoot,
final LogsBloomFilter logsBloom,
final Difficulty difficulty,
final long number,
final long gasLimit,
final long gasUsed,
final long timestamp,
final Bytes extraData,
final Wei baseFee,
final Bytes32 mixHashOrPrevRandao,
final long nonce,
final Hash withdrawalsRoot,
final Long blobGasUsed,
final BlobGas excessBlobGas,
final Bytes32 parentBeaconBlockRoot,
final Hash requestsHash,
final BlockHeaderFunctions blockHeaderFunctions,
final Optional<Bytes> rawRlp) {
super(
parentHash,
ommersHash,
@@ -90,6 +143,7 @@ public class BlockHeader extends SealableBlockHeader
this.nonce = nonce;
this.hash = Suppliers.memoize(() -> blockHeaderFunctions.hash(this));
this.parsedExtraData = Suppliers.memoize(() -> blockHeaderFunctions.parseExtraData(this));
this.rawRlp = rawRlp;
}
public static boolean hasEmptyBlock(final BlockHeader blockHeader) {
@@ -154,72 +208,80 @@ public class BlockHeader extends SealableBlockHeader
* @param out The RLP output to write to
*/
public void writeTo(final RLPOutput out) {
out.startList();
rawRlp.ifPresentOrElse(
out::writeRLPBytes,
() -> {
out.startList();
out.writeBytes(parentHash);
out.writeBytes(ommersHash);
out.writeBytes(coinbase);
out.writeBytes(stateRoot);
out.writeBytes(transactionsRoot);
out.writeBytes(receiptsRoot);
out.writeBytes(logsBloom);
out.writeUInt256Scalar(difficulty);
out.writeLongScalar(number);
out.writeLongScalar(gasLimit);
out.writeLongScalar(gasUsed);
out.writeLongScalar(timestamp);
out.writeBytes(extraData);
out.writeBytes(mixHashOrPrevRandao);
out.writeLong(nonce);
do {
if (baseFee == null) break;
out.writeUInt256Scalar(baseFee);
out.writeBytes(parentHash);
out.writeBytes(ommersHash);
out.writeBytes(coinbase);
out.writeBytes(stateRoot);
out.writeBytes(transactionsRoot);
out.writeBytes(receiptsRoot);
out.writeBytes(logsBloom);
out.writeUInt256Scalar(difficulty);
out.writeLongScalar(number);
out.writeLongScalar(gasLimit);
out.writeLongScalar(gasUsed);
out.writeLongScalar(timestamp);
out.writeBytes(extraData);
out.writeBytes(mixHashOrPrevRandao);
out.writeLong(nonce);
do {
if (baseFee == null) break;
out.writeUInt256Scalar(baseFee);
if (withdrawalsRoot == null) break;
out.writeBytes(withdrawalsRoot);
if (withdrawalsRoot == null) break;
out.writeBytes(withdrawalsRoot);
if (excessBlobGas == null || blobGasUsed == null) break;
out.writeLongScalar(blobGasUsed);
out.writeUInt64Scalar(excessBlobGas);
if (excessBlobGas == null || blobGasUsed == null) break;
out.writeLongScalar(blobGasUsed);
out.writeUInt64Scalar(excessBlobGas);
if (parentBeaconBlockRoot == null) break;
out.writeBytes(parentBeaconBlockRoot);
if (parentBeaconBlockRoot == null) break;
out.writeBytes(parentBeaconBlockRoot);
if (requestsHash == null) break;
out.writeBytes(requestsHash);
} while (false);
out.endList();
if (requestsHash == null) break;
out.writeBytes(requestsHash);
} while (false);
out.endList();
});
}
public static BlockHeader readFrom(
final RLPInput input, final BlockHeaderFunctions blockHeaderFunctions) {
input.enterList();
final Hash parentHash = Hash.wrap(input.readBytes32());
final Hash ommersHash = Hash.wrap(input.readBytes32());
final Address coinbase = Address.readFrom(input);
final Hash stateRoot = Hash.wrap(input.readBytes32());
final Hash transactionsRoot = Hash.wrap(input.readBytes32());
final Hash receiptsRoot = Hash.wrap(input.readBytes32());
final LogsBloomFilter logsBloom = LogsBloomFilter.readFrom(input);
final Difficulty difficulty = Difficulty.of(input.readUInt256Scalar());
final long number = input.readLongScalar();
final long gasLimit = input.readLongScalar();
final long gasUsed = input.readLongScalar();
final long timestamp = input.readLongScalar();
final Bytes extraData = input.readBytes();
final Bytes32 mixHashOrPrevRandao = input.readBytes32();
final long nonce = input.readLong();
final Wei baseFee = !input.isEndOfCurrentList() ? Wei.of(input.readUInt256Scalar()) : null;
final RLPInput headerRlp = input.readAsRlp();
headerRlp.enterList();
final Hash parentHash = Hash.wrap(headerRlp.readBytes32());
final Hash ommersHash = Hash.wrap(headerRlp.readBytes32());
final Address coinbase = Address.readFrom(headerRlp);
final Hash stateRoot = Hash.wrap(headerRlp.readBytes32());
final Hash transactionsRoot = Hash.wrap(headerRlp.readBytes32());
final Hash receiptsRoot = Hash.wrap(headerRlp.readBytes32());
final LogsBloomFilter logsBloom = LogsBloomFilter.readFrom(headerRlp);
final Difficulty difficulty = Difficulty.of(headerRlp.readUInt256Scalar());
final long number = headerRlp.readLongScalar();
final long gasLimit = headerRlp.readLongScalar();
final long gasUsed = headerRlp.readLongScalar();
final long timestamp = headerRlp.readLongScalar();
final Bytes extraData = headerRlp.readBytes();
final Bytes32 mixHashOrPrevRandao = headerRlp.readBytes32();
final long nonce = headerRlp.readLong();
final Wei baseFee =
!headerRlp.isEndOfCurrentList() ? Wei.of(headerRlp.readUInt256Scalar()) : null;
final Hash withdrawalHashRoot =
!(input.isEndOfCurrentList() || input.isZeroLengthString())
? Hash.wrap(input.readBytes32())
!(headerRlp.isEndOfCurrentList() || headerRlp.isZeroLengthString())
? Hash.wrap(headerRlp.readBytes32())
: null;
final Long blobGasUsed = !input.isEndOfCurrentList() ? input.readLongScalar() : null;
final Long blobGasUsed = !headerRlp.isEndOfCurrentList() ? headerRlp.readLongScalar() : null;
final BlobGas excessBlobGas =
!input.isEndOfCurrentList() ? BlobGas.of(input.readUInt64Scalar()) : null;
final Bytes32 parentBeaconBlockRoot = !input.isEndOfCurrentList() ? input.readBytes32() : null;
final Hash requestsHash = !input.isEndOfCurrentList() ? Hash.wrap(input.readBytes32()) : null;
input.leaveList();
!headerRlp.isEndOfCurrentList() ? BlobGas.of(headerRlp.readUInt64Scalar()) : null;
final Bytes32 parentBeaconBlockRoot =
!headerRlp.isEndOfCurrentList() ? headerRlp.readBytes32() : null;
final Hash requestsHash =
!headerRlp.isEndOfCurrentList() ? Hash.wrap(headerRlp.readBytes32()) : null;
headerRlp.leaveList();
return new BlockHeader(
parentHash,
ommersHash,
@@ -242,7 +304,8 @@ public class BlockHeader extends SealableBlockHeader
excessBlobGas,
parentBeaconBlockRoot,
requestsHash,
blockHeaderFunctions);
blockHeaderFunctions,
Optional.of(headerRlp.raw()));
}
@Override

View File

@@ -95,7 +95,7 @@ public class CodeDelegation implements org.hyperledger.besu.datatypes.CodeDelega
Bytes.fromHexStringLenient(nonce).toLong(),
SIGNATURE_ALGORITHM
.get()
.createSignature(
.createCodeDelegationSignature(
Bytes.fromHexStringLenient(r).toUnsignedBigInteger(),
Bytes.fromHexStringLenient(s).toUnsignedBigInteger(),
Bytes.fromHexStringLenient(v).get(0)));
@@ -121,6 +121,12 @@ public class CodeDelegation implements org.hyperledger.besu.datatypes.CodeDelega
@Override
public Optional<Address> authorizer() {
// recId needs to be between 0 and 3, otherwise the signature is invalid
// which means we can't recover the authorizer.
if (signature.getRecId() < 0 || signature.getRecId() > 3) {
return Optional.empty();
}
return authorizerSupplier.get();
}
@@ -272,4 +278,20 @@ public class CodeDelegation implements org.hyperledger.besu.datatypes.CodeDelega
return new CodeDelegation(chainId, address, nonce, signature);
}
}
@Override
public String toString() {
return "CodeDelegation{"
+ "chainId="
+ chainId
+ ", address="
+ address
+ ", nonce="
+ nonce
+ ", signature="
+ signature
+ ", authorizerSupplier="
+ authorizerSupplier
+ '}';
}
}

View File

@@ -126,6 +126,8 @@ public class Transaction
private final Optional<BlobsWithCommitments> blobsWithCommitments;
private final Optional<List<CodeDelegation>> maybeCodeDelegationList;
private final Optional<Bytes> rawRlp;
public static Builder builder() {
return new Builder();
}
@@ -181,7 +183,8 @@ public class Transaction
final Optional<BigInteger> chainId,
final Optional<List<VersionedHash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments,
final Optional<List<CodeDelegation>> maybeCodeDelegationList) {
final Optional<List<CodeDelegation>> maybeCodeDelegationList,
final Optional<Bytes> rawRlp) {
if (!forCopy) {
if (transactionType.requiresChainId()) {
@@ -242,6 +245,7 @@ public class Transaction
this.versionedHashes = versionedHashes;
this.blobsWithCommitments = blobsWithCommitments;
this.maybeCodeDelegationList = maybeCodeDelegationList;
this.rawRlp = rawRlp;
}
/**
@@ -665,6 +669,10 @@ public class Transaction
return getEffectivePriorityFeePerGas(baseFeePerGas).addExact(baseFeePerGas.orElse(Wei.ZERO));
}
public Optional<Bytes> getRawRlp() {
return rawRlp;
}
@Override
public TransactionType getType() {
return this.transactionType;
@@ -1116,7 +1124,8 @@ public class Transaction
chainId,
detachedVersionedHashes,
detachedBlobsWithCommitments,
detachedCodeDelegationList);
detachedCodeDelegationList,
Optional.empty());
// copy also the computed fields, to avoid to recompute them
copiedTx.sender = this.sender;
@@ -1194,6 +1203,7 @@ public class Transaction
protected List<VersionedHash> versionedHashes = null;
private BlobsWithCommitments blobsWithCommitments;
protected Optional<List<CodeDelegation>> codeDelegationAuthorizations = Optional.empty();
protected Bytes rawRlp = null;
public Builder copiedFrom(final Transaction toCopy) {
this.transactionType = toCopy.transactionType;
@@ -1299,6 +1309,11 @@ public class Transaction
return this;
}
public Builder rawRlp(final Bytes rawRlp) {
this.rawRlp = rawRlp;
return this;
}
public Builder guessType() {
if (codeDelegationAuthorizations.isPresent()) {
transactionType = TransactionType.DELEGATE_CODE;
@@ -1338,7 +1353,8 @@ public class Transaction
chainId,
Optional.ofNullable(versionedHashes),
Optional.ofNullable(blobsWithCommitments),
codeDelegationAuthorizations);
codeDelegationAuthorizations,
Optional.ofNullable(rawRlp));
}
public Transaction signAndBuild(final KeyPair keys) {

View File

@@ -37,21 +37,23 @@ class AccessListTransactionDecoder {
}
public static Transaction decode(final RLPInput rlpInput) {
rlpInput.enterList();
RLPInput transactionRlp = rlpInput.readAsRlp();
transactionRlp.enterList();
final Transaction.Builder preSignatureTransactionBuilder =
Transaction.builder()
.type(TransactionType.ACCESS_LIST)
.chainId(BigInteger.valueOf(rlpInput.readLongScalar()))
.nonce(rlpInput.readLongScalar())
.gasPrice(Wei.of(rlpInput.readUInt256Scalar()))
.gasLimit(rlpInput.readLongScalar())
.chainId(BigInteger.valueOf(transactionRlp.readLongScalar()))
.nonce(transactionRlp.readLongScalar())
.gasPrice(Wei.of(transactionRlp.readUInt256Scalar()))
.gasLimit(transactionRlp.readLongScalar())
.to(
rlpInput.readBytes(
transactionRlp.readBytes(
addressBytes -> addressBytes.isEmpty() ? null : Address.wrap(addressBytes)))
.value(Wei.of(rlpInput.readUInt256Scalar()))
.payload(rlpInput.readBytes())
.value(Wei.of(transactionRlp.readUInt256Scalar()))
.payload(transactionRlp.readBytes())
.rawRlp(transactionRlp.raw())
.accessList(
rlpInput.readList(
transactionRlp.readList(
accessListEntryRLPInput -> {
accessListEntryRLPInput.enterList();
final AccessListEntry accessListEntry =
@@ -61,18 +63,18 @@ class AccessListTransactionDecoder {
accessListEntryRLPInput.leaveList();
return accessListEntry;
}));
final byte recId = (byte) rlpInput.readUnsignedByteScalar();
final byte recId = (byte) transactionRlp.readUnsignedByteScalar();
final Transaction transaction =
preSignatureTransactionBuilder
.signature(
SIGNATURE_ALGORITHM
.get()
.createSignature(
rlpInput.readUInt256Scalar().toUnsignedBigInteger(),
rlpInput.readUInt256Scalar().toUnsignedBigInteger(),
transactionRlp.readUInt256Scalar().toUnsignedBigInteger(),
transactionRlp.readUInt256Scalar().toUnsignedBigInteger(),
recId))
.build();
rlpInput.leaveList();
transactionRlp.leaveList();
return transaction;
}
}

View File

@@ -39,21 +39,23 @@ public class CodeDelegationTransactionDecoder {
}
public static Transaction decode(final RLPInput input) {
input.enterList();
final BigInteger chainId = input.readBigIntegerScalar();
RLPInput transactionRlp = input.readAsRlp();
transactionRlp.enterList();
final BigInteger chainId = transactionRlp.readBigIntegerScalar();
final Transaction.Builder builder =
Transaction.builder()
.type(TransactionType.DELEGATE_CODE)
.chainId(chainId)
.nonce(input.readLongScalar())
.maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar()))
.maxFeePerGas(Wei.of(input.readUInt256Scalar()))
.gasLimit(input.readLongScalar())
.to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v)))
.value(Wei.of(input.readUInt256Scalar()))
.payload(input.readBytes())
.nonce(transactionRlp.readLongScalar())
.maxPriorityFeePerGas(Wei.of(transactionRlp.readUInt256Scalar()))
.maxFeePerGas(Wei.of(transactionRlp.readUInt256Scalar()))
.gasLimit(transactionRlp.readLongScalar())
.to(transactionRlp.readBytes(v -> v.isEmpty() ? null : Address.wrap(v)))
.value(Wei.of(transactionRlp.readUInt256Scalar()))
.payload(transactionRlp.readBytes())
.rawRlp(transactionRlp.raw())
.accessList(
input.readList(
transactionRlp.readList(
accessListEntryRLPInput -> {
accessListEntryRLPInput.enterList();
final AccessListEntry accessListEntry =
@@ -63,13 +65,14 @@ public class CodeDelegationTransactionDecoder {
accessListEntryRLPInput.leaveList();
return accessListEntry;
}))
.codeDelegations(input.readList(CodeDelegationTransactionDecoder::decodeInnerPayload));
.codeDelegations(
transactionRlp.readList(CodeDelegationTransactionDecoder::decodeInnerPayload));
final byte recId = (byte) input.readUnsignedByteScalar();
final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger();
final byte recId = (byte) transactionRlp.readUnsignedByteScalar();
final BigInteger r = transactionRlp.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = transactionRlp.readUInt256Scalar().toUnsignedBigInteger();
input.leaveList();
transactionRlp.leaveList();
return builder.signature(SIGNATURE_ALGORITHM.get().createSignature(r, s, recId)).build();
}

View File

@@ -37,21 +37,23 @@ public class EIP1559TransactionDecoder {
}
public static Transaction decode(final RLPInput input) {
input.enterList();
final BigInteger chainId = input.readBigIntegerScalar();
RLPInput transactionRlp = input.readAsRlp();
transactionRlp.enterList();
final BigInteger chainId = transactionRlp.readBigIntegerScalar();
final Transaction.Builder builder =
Transaction.builder()
.type(TransactionType.EIP1559)
.chainId(chainId)
.nonce(input.readLongScalar())
.maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar()))
.maxFeePerGas(Wei.of(input.readUInt256Scalar()))
.gasLimit(input.readLongScalar())
.to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v)))
.value(Wei.of(input.readUInt256Scalar()))
.payload(input.readBytes())
.nonce(transactionRlp.readLongScalar())
.maxPriorityFeePerGas(Wei.of(transactionRlp.readUInt256Scalar()))
.maxFeePerGas(Wei.of(transactionRlp.readUInt256Scalar()))
.gasLimit(transactionRlp.readLongScalar())
.to(transactionRlp.readBytes(v -> v.isEmpty() ? null : Address.wrap(v)))
.value(Wei.of(transactionRlp.readUInt256Scalar()))
.payload(transactionRlp.readBytes())
.rawRlp(transactionRlp.raw())
.accessList(
input.readList(
transactionRlp.readList(
accessListEntryRLPInput -> {
accessListEntryRLPInput.enterList();
final AccessListEntry accessListEntry =
@@ -61,18 +63,18 @@ public class EIP1559TransactionDecoder {
accessListEntryRLPInput.leaveList();
return accessListEntry;
}));
final byte recId = (byte) input.readUnsignedByteScalar();
final byte recId = (byte) transactionRlp.readUnsignedByteScalar();
final Transaction transaction =
builder
.signature(
SIGNATURE_ALGORITHM
.get()
.createSignature(
input.readUInt256Scalar().toUnsignedBigInteger(),
input.readUInt256Scalar().toUnsignedBigInteger(),
transactionRlp.readUInt256Scalar().toUnsignedBigInteger(),
transactionRlp.readUInt256Scalar().toUnsignedBigInteger(),
recId))
.build();
input.leaveList();
transactionRlp.leaveList();
return transaction;
}
}

View File

@@ -41,18 +41,20 @@ public class FrontierTransactionDecoder {
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
public static Transaction decode(final RLPInput input) {
input.enterList();
RLPInput transactionRlp = input.readAsRlp();
transactionRlp.enterList();
final Transaction.Builder builder =
Transaction.builder()
.type(TransactionType.FRONTIER)
.nonce(input.readLongScalar())
.gasPrice(Wei.of(input.readUInt256Scalar()))
.gasLimit(input.readLongScalar())
.to(input.readBytes(v -> v.size() == 0 ? null : Address.wrap(v)))
.value(Wei.of(input.readUInt256Scalar()))
.payload(input.readBytes());
.nonce(transactionRlp.readLongScalar())
.gasPrice(Wei.of(transactionRlp.readUInt256Scalar()))
.gasLimit(transactionRlp.readLongScalar())
.to(transactionRlp.readBytes(v -> v.size() == 0 ? null : Address.wrap(v)))
.value(Wei.of(transactionRlp.readUInt256Scalar()))
.payload(transactionRlp.readBytes())
.rawRlp(transactionRlp.raw());
final BigInteger v = input.readBigIntegerScalar();
final BigInteger v = transactionRlp.readBigIntegerScalar();
final byte recId;
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
@@ -64,11 +66,11 @@ public class FrontierTransactionDecoder {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
}
final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger r = transactionRlp.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = transactionRlp.readUInt256Scalar().toUnsignedBigInteger();
final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, recId);
input.leaveList();
transactionRlp.leaveList();
chainId.ifPresent(builder::chainId);
return builder.signature(signature).build();

View File

@@ -58,6 +58,7 @@ public class TransactionEncoder {
final RLPOutput rlpOutput,
final EncodingContext encodingContext) {
final TransactionType transactionType = getTransactionType(transaction);
Bytes opaqueBytes = encodeOpaqueBytes(transaction, encodingContext);
encodeRLP(transactionType, opaqueBytes, rlpOutput);
}
@@ -94,8 +95,16 @@ public class TransactionEncoder {
} else {
final Encoder encoder = getEncoder(transactionType, encodingContext);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.writeByte(transaction.getType().getSerializedType());
encoder.encode(transaction, out);
transaction
.getRawRlp()
.ifPresentOrElse(
(rawRlp) ->
out.writeRLPBytes(
Bytes.concatenate(Bytes.of(transactionType.getSerializedType()), rawRlp)),
() -> {
out.writeByte(transaction.getType().getSerializedType());
encoder.encode(transaction, out);
});
return out.encoded();
}
}

View File

@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet;
import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
@@ -191,6 +192,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
}
blockUpdater.commit();
blockUpdater.markTransactionBoundary();
currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining();
if (transaction.getVersionedHashes().isPresent()) {
@@ -250,6 +252,19 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
maybeRequests = Optional.of(requestProcessor.get().process(context));
}
if (maybeRequests.isPresent() && blockHeader.getRequestsHash().isPresent()) {
Hash calculatedRequestHash = BodyValidation.requestsHash(maybeRequests.get());
Hash headerRequestsHash = blockHeader.getRequestsHash().get();
if (!calculatedRequestHash.equals(headerRequestsHash)) {
return new BlockProcessingResult(
Optional.empty(),
"Requests hash mismatch, calculated: "
+ calculatedRequestHash.toHexString()
+ " header: "
+ headerRequestsHash.toHexString());
}
}
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) {
// no need to log, rewardCoinbase logs the error.
if (worldState instanceof BonsaiWorldState) {

View File

@@ -105,11 +105,6 @@ public class CodeDelegationProcessor {
return;
}
if (codeDelegation.signature().getRecId() != 0 && codeDelegation.signature().getRecId() != 1) {
LOG.trace("Invalid signature for code delegation. RecId must be 0 or 1.");
return;
}
final Optional<Address> authorizer = codeDelegation.authorizer();
if (authorizer.isEmpty()) {
LOG.trace("Invalid signature for code delegation");
@@ -126,6 +121,10 @@ public class CodeDelegationProcessor {
MutableAccount authority;
boolean authorityDoesAlreadyExist = false;
if (maybeAuthorityAccount.isEmpty()) {
// only create an account if nonce is valid
if (codeDelegation.nonce() != 0) {
return;
}
authority = evmWorldUpdater.createAccount(authorizer.get());
} else {
authority = maybeAuthorityAccount.get();
@@ -146,7 +145,7 @@ public class CodeDelegationProcessor {
}
if (authorityDoesAlreadyExist) {
result.incremenentAlreadyExistingDelegators();
result.incrementAlreadyExistingDelegators();
}
evmWorldUpdater

View File

@@ -27,7 +27,7 @@ public class CodeDelegationResult {
accessedDelegatorAddresses.add(address);
}
public void incremenentAlreadyExistingDelegators() {
public void incrementAlreadyExistingDelegators() {
alreadyExistingDelegators += 1;
}

View File

@@ -282,7 +282,7 @@ public class ProtocolSpec {
}
/**
* Returns the TransctionReceiptFactory used in this specification
* Returns the TransactionReceiptFactory used in this specification
*
* @return the transaction receipt factory
*/

View File

@@ -35,7 +35,7 @@ public class PragueBlockHashProcessor extends CancunBlockHashProcessor {
private static final Logger LOG = LoggerFactory.getLogger(PragueBlockHashProcessor.class);
public static final Address HISTORY_STORAGE_ADDRESS =
Address.fromHexString("0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC");
Address.fromHexString("0x0000f90827f1c53a10cb7a02335b175320002935");
/** The HISTORY_SERVE_WINDOW */
private static final long HISTORY_SERVE_WINDOW = 8191;
@@ -68,9 +68,11 @@ public class PragueBlockHashProcessor extends CancunBlockHashProcessor {
super.processBlockHashes(mutableWorldState, currentBlockHeader);
WorldUpdater worldUpdater = mutableWorldState.updater();
final MutableAccount historyStorageAccount = worldUpdater.getOrCreate(historyStorageAddress);
final MutableAccount historyStorageAccount = worldUpdater.getAccount(historyStorageAddress);
if (currentBlockHeader.getNumber() > 0) {
if (historyStorageAccount != null
&& historyStorageAccount.getNonce() > 0
&& currentBlockHeader.getNumber() > 0) {
storeParentHash(historyStorageAccount, currentBlockHeader);
}
worldUpdater.commit();

View File

@@ -14,12 +14,14 @@
*/
package org.hyperledger.besu.ethereum.mainnet.requests;
import org.hyperledger.besu.datatypes.RequestType;
import org.hyperledger.besu.ethereum.core.Request;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.Ordering;
import com.google.common.collect.Comparators;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,15 +48,25 @@ public class MainnetRequestsValidator implements RequestsValidator {
return false;
}
if (!isRequestOrderValid(maybeRequests.get())) {
LOG.warn("Ordering across requests must be ascending by type");
List<Request> requests = maybeRequests.get();
if (!areRequestTypesUniqueAndOrderValid(requests)) {
LOG.warn("Request types must be unique and ordering must be ascending by type");
return false;
}
if (containsRequestWithEmptyData(requests)) {
LOG.warn("Request must not be empty");
return false;
}
return true;
}
private static boolean isRequestOrderValid(final List<Request> requests) {
return Ordering.natural().onResultOf(Request::getType).isOrdered(requests);
private static boolean areRequestTypesUniqueAndOrderValid(final List<Request> requests) {
final List<RequestType> requestTypes = requests.stream().map(Request::type).toList();
return Comparators.isInStrictOrder(requestTypes, Comparator.naturalOrder());
}
private static boolean containsRequestWithEmptyData(final List<Request> requests) {
return requests.stream().anyMatch(request -> request.getData().isEmpty());
}
}

View File

@@ -23,9 +23,9 @@ public class RequestContractAddresses {
private final Address consolidationRequestContractAddress;
public static final Address DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS =
Address.fromHexString("0x0c15F14308530b7CDB8460094BbB9cC28b9AaaAA");
Address.fromHexString("0x00000961ef480eb55e80d19ad83579a64c007002");
public static final Address DEFAULT_CONSOLIDATION_REQUEST_CONTRACT_ADDRESS =
Address.fromHexString("0x00431F263cE400f4455c2dCf564e53007Ca4bbBb");
Address.fromHexString("0x0000bbddc7ce488642fb579f8b00f3a590007251");
public static final Address DEFAULT_DEPOSIT_CONTRACT_ADDRESS =
Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa");

View File

@@ -54,7 +54,6 @@ import java.util.Optional;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
/**
* Simulates the execution of a block, processing transactions and applying state overrides. This
@@ -248,19 +247,7 @@ public class BlockSimulator {
for (Address accountToOverride : stateOverrideMap.keySet()) {
final StateOverride override = stateOverrideMap.get(accountToOverride);
MutableAccount account = updater.getOrCreate(accountToOverride);
override.getNonce().ifPresent(account::setNonce);
if (override.getBalance().isPresent()) {
account.setBalance(override.getBalance().get());
}
override.getCode().ifPresent(n -> account.setCode(Bytes.fromHexString(n)));
override
.getStateDiff()
.ifPresent(
d ->
d.forEach(
(key, value) ->
account.setStorageValue(
UInt256.fromHexString(key), UInt256.fromHexString(value))));
TransactionSimulator.applyOverrides(account, override);
}
updater.commit();
}

View File

@@ -460,13 +460,21 @@ public class TransactionSimulator {
}
@VisibleForTesting
protected void applyOverrides(final MutableAccount account, final StateOverride override) {
protected static void applyOverrides(final MutableAccount account, final StateOverride override) {
LOG.debug("applying overrides to state for account {}", account.getAddress());
override.getNonce().ifPresent(account::setNonce);
if (override.getBalance().isPresent()) {
account.setBalance(override.getBalance().get());
}
override.getCode().ifPresent(n -> account.setCode(Bytes.fromHexString(n)));
override.getBalance().ifPresent(account::setBalance);
override.getCode().ifPresent(code -> account.setCode(Bytes.fromHexString(code)));
override
.getState()
.ifPresent(
d -> {
account.clearStorage();
d.forEach(
(key, value) ->
account.setStorageValue(
UInt256.fromHexString(key), UInt256.fromHexString(value)));
});
override
.getStateDiff()
.ifPresent(

View File

@@ -499,8 +499,6 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
tracked.setStorageWasCleared(false); // storage already cleared for this transaction
}
});
getUpdatedAccounts().clear();
getDeletedAccounts().clear();
}
@Override
@@ -598,6 +596,21 @@ public abstract class DiffBasedWorldStateUpdateAccumulator<ACCOUNT extends DiffB
return results;
}
/**
* Marks the boundary of a transaction by clearing tracking collections.
*
* <p>These tracking collections store changes made during the transaction. After committing the
* transaction, they become unnecessary and can be safely cleared.
*
* <p>Note: If the transaction is not committed before this method is called, any uncommitted
* changes will be lost.
*/
@Override
public void markTransactionBoundary() {
getUpdatedAccounts().clear();
getDeletedAccounts().clear();
}
@Override
public boolean isModifyingHeadWorldState() {
return true;

View File

@@ -0,0 +1,169 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.core;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import java.math.BigInteger;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
class CodeDelegationTest {
private BigInteger chainId;
private Address address;
private long nonce;
private SECPSignature signature;
private SignatureAlgorithm signatureAlgorithm;
@BeforeEach
void setUp() {
chainId = BigInteger.valueOf(1);
address = Address.fromHexString("0x1234567890abcdef1234567890abcdef12345678");
nonce = 100;
signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
KeyPair keyPair = signatureAlgorithm.generateKeyPair();
signature = signatureAlgorithm.sign(Bytes32.fromHexStringLenient("deadbeef"), keyPair);
}
@Test
void shouldCreateCodeDelegationSuccessfully() {
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, signature);
assertThat(delegation.chainId()).isEqualTo(chainId);
assertThat(delegation.address()).isEqualTo(address);
assertThat(delegation.nonce()).isEqualTo(nonce);
assertThat(delegation.signature()).isEqualTo(signature);
}
@Test
void shouldBuildCodeDelegationWithBuilder() {
CodeDelegation delegation =
(CodeDelegation)
CodeDelegation.builder()
.chainId(chainId)
.address(address)
.nonce(nonce)
.signature(signature)
.build();
assertThat(delegation).isNotNull();
assertThat(delegation.chainId()).isEqualTo(chainId);
assertThat(delegation.address()).isEqualTo(address);
assertThat(delegation.nonce()).isEqualTo(nonce);
assertThat(delegation.signature()).isEqualTo(signature);
}
@Test
void shouldThrowWhenBuildingWithoutAddress() {
assertThatThrownBy(
() ->
CodeDelegation.builder().chainId(chainId).nonce(nonce).signature(signature).build())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Address must be set");
}
@Test
void shouldThrowWhenBuildingWithoutNonce() {
assertThatThrownBy(
() ->
CodeDelegation.builder()
.chainId(chainId)
.address(address)
.signature(signature)
.build())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Nonce must be set");
}
@Test
void shouldThrowWhenBuildingWithoutSignature() {
assertThatThrownBy(
() -> CodeDelegation.builder().chainId(chainId).address(address).nonce(nonce).build())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Signature must be set");
}
@Test
void shouldCreateCodeDelegationUsingFactoryMethod() {
CodeDelegation delegation =
(CodeDelegation)
CodeDelegation.createCodeDelegation(
chainId, address, "0x64", "0x1b", "0xabcdef", "0x123456");
assertThat(delegation).isNotNull();
assertThat(delegation.chainId()).isEqualTo(chainId);
assertThat(delegation.address()).isEqualTo(address);
assertThat(delegation.nonce()).isEqualTo(100);
}
@Test
void shouldReturnAuthorizerWhenSignatureIsValid() {
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, signature);
Optional<Address> authorizer = delegation.authorizer();
assertThat(authorizer).isNotEmpty();
}
@Test
void shouldReturnEmptyAuthorizerWhenSignatureInvalid() {
SECPSignature invalidSignature = Mockito.mock(SECPSignature.class);
Mockito.when(invalidSignature.getRecId()).thenReturn((byte) 5); // Invalid recId (>3)
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, invalidSignature);
Optional<Address> authorizer = delegation.authorizer();
assertThat(authorizer).isEmpty();
}
@Test
void shouldReturnCorrectSignatureValues() {
CodeDelegation delegation = new CodeDelegation(chainId, address, nonce, signature);
assertThat(delegation.v()).isEqualTo(signature.getRecId());
assertThat(delegation.r()).isEqualTo(signature.getR());
assertThat(delegation.s()).isEqualTo(signature.getS());
}
@Test
void shouldSignAndBuildUsingKeyPair() {
KeyPair keyPair = signatureAlgorithm.generateKeyPair();
CodeDelegation delegation =
(CodeDelegation)
CodeDelegation.builder()
.chainId(chainId)
.address(address)
.nonce(nonce)
.signAndBuild(keyPair);
assertThat(delegation).isNotNull();
assertThat(delegation.signature()).isNotNull();
}
}

View File

@@ -19,7 +19,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPException;
@@ -79,6 +81,20 @@ class TransactionRLPDecoderTest {
assertThat(transaction.getNonce()).isEqualTo(MAX_NONCE - 1);
}
@Test
void testForAccessListTransaction() {
BlockDataGenerator gen = new BlockDataGenerator();
Transaction accessListTransaction = gen.transaction(TransactionType.ACCESS_LIST);
Bytes encodedBytes =
TransactionEncoder.encodeOpaqueBytes(accessListTransaction, EncodingContext.BLOCK_BODY);
Transaction decodedTransaction =
TransactionDecoder.decodeOpaqueBytes(encodedBytes, EncodingContext.BLOCK_BODY);
assertThat(accessListTransaction).isEqualTo(decodedTransaction);
Bytes reencodedBytes =
TransactionEncoder.encodeOpaqueBytes(decodedTransaction, EncodingContext.BLOCK_BODY);
assertThat(encodedBytes).isEqualTo(reencodedBytes);
}
private static Collection<Object[]> dataTransactionSize() {
return Arrays.asList(
new Object[][] {

View File

@@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -112,6 +113,23 @@ class CodeDelegationProcessorTest {
verify(delegationCodeService).processCodeDelegation(authority, DELEGATE_ADDRESS);
}
@Test
void shouldNotCreateAccountIfNonceIsInvalid() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 1L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(null);
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(worldUpdater, never()).createAccount(any());
verify(authority, never()).incrementNonce();
verify(delegationCodeService, never()).processCodeDelegation(authority, DELEGATE_ADDRESS);
}
@Test
void shouldProcessValidDelegationForExistingAccount() {
// Arrange
@@ -150,6 +168,51 @@ class CodeDelegationProcessorTest {
verify(delegationCodeService, never()).processCodeDelegation(any(), any());
}
@Test
void shouldSkipOverInvalidMultipleInvalidNonceDelegationsForSameAuthorityForNewAccount() {
// Arrange
when(worldUpdater.codeDelegationService()).thenReturn(delegationCodeService);
var signature1 = new SECPSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
long cd1_invalidNonce = 2L;
var cd1_invalid =
new org.hyperledger.besu.ethereum.core.CodeDelegation(
CHAIN_ID,
Address.fromHexString("0x0000000000000000000000000000000000001000"),
cd1_invalidNonce,
signature1);
var signature2 = new SECPSignature(BigInteger.TWO, BigInteger.TWO, (byte) 0);
final long cd2_validNonce = 0L;
var cd2_valid =
new org.hyperledger.besu.ethereum.core.CodeDelegation(
CHAIN_ID,
Address.fromHexString("0x0000000000000000000000000000000000001100"),
cd2_validNonce,
signature2);
var signature3 = new SECPSignature(BigInteger.TWO, BigInteger.TWO, (byte) 0);
final long cd3_invalidNonce = 0L;
var cd3_invalid =
new org.hyperledger.besu.ethereum.core.CodeDelegation(
CHAIN_ID,
Address.fromHexString("0x0000000000000000000000000000000000001200"),
cd3_invalidNonce,
signature3);
when(transaction.getCodeDelegationList())
.thenReturn(Optional.of(List.of(cd1_invalid, cd2_valid, cd3_invalid)));
when(worldUpdater.getAccount(any())).thenReturn(null).thenReturn(null).thenReturn(authority);
when(worldUpdater.createAccount(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(0L).thenReturn(1L);
when(delegationCodeService.canSetCodeDelegation(any())).thenReturn(true);
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(authority, times(1)).incrementNonce();
verify(delegationCodeService, times(1)).processCodeDelegation(any(), any());
}
@Test
void shouldRejectDelegationWithSGreaterThanHalfCurveOrder() {
// Arrange

View File

@@ -45,8 +45,9 @@ class BlockHashProcessorTest {
mutableWorldState = mock(MutableWorldState.class);
worldUpdater = mock(WorldUpdater.class);
account = mock(MutableAccount.class);
when(account.getNonce()).thenReturn(1L);
when(mutableWorldState.updater()).thenReturn(worldUpdater);
when(worldUpdater.getOrCreate(PragueBlockHashProcessor.HISTORY_STORAGE_ADDRESS))
when(worldUpdater.getAccount(PragueBlockHashProcessor.HISTORY_STORAGE_ADDRESS))
.thenReturn(account);
}
@@ -72,7 +73,7 @@ class BlockHashProcessorTest {
mockAncestorHeaders(currentBlockHeader, 0);
processor.processBlockHashes(mutableWorldState, currentBlockHeader);
verifyNoInteractions(account);
verify(account, times(0)).setStorageValue(any(), any());
}
@Test
@@ -89,6 +90,20 @@ class BlockHashProcessorTest {
verifyAccount(0, historicalWindow);
}
@Test
void shouldNotStoreBlockHashIfContractIsNotDeployed() {
when(worldUpdater.getAccount(PragueBlockHashProcessor.HISTORY_STORAGE_ADDRESS))
.thenReturn(null);
long currentBlock = 1;
processor = new PragueBlockHashProcessor();
BlockHeader currentBlockHeader = mockBlockHeader(currentBlock);
mockAncestorHeaders(currentBlockHeader, 0);
processor.processBlockHashes(mutableWorldState, currentBlockHeader);
verifyNoInteractions(account);
}
@Test
void shouldWriteGenesisHashAtSlot0() {
processor = new PragueBlockHashProcessor();

View File

@@ -55,4 +55,21 @@ class MainnetRequestsValidatorTest {
new Request(RequestType.CONSOLIDATION, Bytes.of(3)));
assertTrue(validator.validate(Optional.of(requests)));
}
@Test
void validateFalseForEmptyRequest() {
MainnetRequestsValidator validator = new MainnetRequestsValidator();
List<Request> requests = List.of(new Request(RequestType.DEPOSIT, Bytes.EMPTY));
assertFalse(validator.validate(Optional.of(requests)));
}
@Test
void validateFalseForDuplicatedRequests() {
MainnetRequestsValidator validator = new MainnetRequestsValidator();
List<Request> requests =
List.of(
new Request(RequestType.DEPOSIT, Bytes.of(1)),
new Request(RequestType.DEPOSIT, Bytes.of(1)));
assertFalse(validator.validate(Optional.of(requests)));
}
}

View File

@@ -30,6 +30,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.StateOverride;
import org.hyperledger.besu.datatypes.StateOverrideMap;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
@@ -50,11 +51,9 @@ import org.hyperledger.besu.plugin.data.BlockOverrides;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
@@ -168,24 +167,25 @@ public class BlockSimulatorTest {
@Test
public void shouldApplyStateOverridesCorrectly() {
StateOverrideMap stateOverrideMap = mock(StateOverrideMap.class);
StateOverrideMap stateOverrideMap = new StateOverrideMap();
Address address = mock(Address.class);
StateOverride stateOverride = mock(StateOverride.class);
MutableAccount mutableAccount = mock(MutableAccount.class);
StateOverride stateOverride =
new StateOverride.Builder()
.withBalance(Wei.of(456L))
.withNonce(new UnsignedLongParameter(123L))
.withCode("")
.withStateDiff(Map.of("0x0", "0x1"))
.build();
when(stateOverrideMap.keySet()).thenReturn(Set.of(address));
when(stateOverrideMap.get(address)).thenReturn(stateOverride);
stateOverrideMap.put(address, stateOverride);
WorldUpdater worldUpdater = mock(WorldUpdater.class);
when(mutableWorldState.updater()).thenReturn(worldUpdater);
MutableAccount mutableAccount = mock(MutableAccount.class);
when(mutableAccount.getAddress()).thenReturn(address);
when(worldUpdater.getOrCreate(address)).thenReturn(mutableAccount);
when(stateOverride.getNonce()).thenReturn(Optional.of(123L));
when(stateOverride.getBalance()).thenReturn(Optional.of(Wei.of(456L)));
when(stateOverride.getCode()).thenReturn(Optional.of(""));
when(stateOverride.getStateDiff()).thenReturn(Optional.of(new HashMap<>(Map.of("0x0", "0x1"))));
blockSimulator.applyStateOverrides(stateOverrideMap, mutableWorldState);
verify(mutableAccount).setNonce(anyLong());

View File

@@ -121,7 +121,7 @@ public class TransactionSimulatorTest {
when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM); // called from logging
StateOverride.Builder builder = new StateOverride.Builder();
StateOverride override = builder.build();
transactionSimulator.applyOverrides(mutableAccount, override);
TransactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount).getAddress();
verifyNoMoreInteractions(mutableAccount);
}
@@ -132,7 +132,7 @@ public class TransactionSimulatorTest {
when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM);
StateOverride.Builder builder = new StateOverride.Builder().withBalance(Wei.of(99));
StateOverride override = builder.build();
transactionSimulator.applyOverrides(mutableAccount, override);
TransactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount).setBalance(eq(Wei.of(99)));
}
@@ -145,7 +145,25 @@ public class TransactionSimulatorTest {
StateOverride.Builder builder =
new StateOverride.Builder().withStateDiff(Map.of(storageKey, storageValue));
StateOverride override = builder.build();
transactionSimulator.applyOverrides(mutableAccount, override);
TransactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount)
.setStorageValue(
eq(UInt256.fromHexString(storageKey)), eq(UInt256.fromHexString(storageValue)));
}
@Test
public void testOverrides_whenStateOverrides_stateIsUpdated() {
MutableAccount mutableAccount = mock(MutableAccount.class);
when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM);
final String storageKey = "0x01a2";
final String storageValue = "0x00ff";
StateOverride.Builder builder =
new StateOverride.Builder().withState(Map.of(storageKey, storageValue));
StateOverride override = builder.build();
TransactionSimulator.applyOverrides(mutableAccount, override);
verify(mutableAccount).clearStorage();
verify(mutableAccount)
.setStorageValue(
eq(UInt256.fromHexString(storageKey)), eq(UInt256.fromHexString(storageValue)));

View File

@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.util;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.hyperledger.besu.datatypes.Address;
@@ -25,6 +26,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import java.util.Map;
import java.util.Optional;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -63,6 +65,7 @@ public class StateOverrideParameterTest {
assertThat(stateOverride.getNonce().get()).isEqualTo(158);
assertThat(stateOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1)));
assertFalse(stateOverride.getState().isPresent());
assertFalse(stateOverride.getStateDiff().isPresent());
}
@@ -91,6 +94,7 @@ public class StateOverrideParameterTest {
assertFalse(stateOverride.getNonce().isPresent());
assertThat(stateOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1)));
assertThat(stateOverride.getCode()).isEqualTo(Optional.of(CODE_STRING));
assertFalse(stateOverride.getState().isPresent());
assertFalse(stateOverride.getStateDiff().isPresent());
}
@@ -118,6 +122,7 @@ public class StateOverrideParameterTest {
assertThat(stateOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1)));
assertThat(stateOverride.getNonce().get()).isEqualTo(158); // 0x9e
assertFalse(stateOverride.getState().isPresent());
assertFalse(stateOverride.getStateDiff().isPresent());
}
@@ -133,7 +138,7 @@ public class StateOverrideParameterTest {
+ "{"
+ "\"balance\": \"0x01\","
+ "\"nonce\": \"0x9E\","
+ "\"stateDiff\": {"
+ "\"state\": {"
+ "\""
+ STORAGE_KEY
+ "\": \""
@@ -150,8 +155,9 @@ public class StateOverrideParameterTest {
final StateOverride stateOverride = stateOverrideParam.get(Address.fromHexString(ADDRESS_HEX1));
assertThat(stateOverride.getNonce().get()).isEqualTo(158);
assertTrue(stateOverride.getStateDiff().isPresent());
assertThat(stateOverride.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
assertTrue(stateOverride.getState().isPresent());
assertThat(stateOverride.getState().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
assertFalse(stateOverride.getStateDiff().isPresent());
}
@Test
@@ -166,7 +172,7 @@ public class StateOverrideParameterTest {
+ "{"
+ "\"balance\": \"0x01\","
+ "\"nonce\": \"0x9E\","
+ "\"stateDiff\": {"
+ "\"state\": {"
+ "\""
+ STORAGE_KEY
+ "\": \""
@@ -179,7 +185,7 @@ public class StateOverrideParameterTest {
+ "{"
+ "\"balance\": \"0xFF\","
+ "\"nonce\": \"0x9D\","
+ "\"stateDiff\": {"
+ "\"state\": {"
+ "\""
+ STORAGE_KEY
+ "\": \""
@@ -197,18 +203,35 @@ public class StateOverrideParameterTest {
stateOverrideParam.get(Address.fromHexString(ADDRESS_HEX1));
assertThat(stateOverride1.getNonce().get()).isEqualTo(158);
assertThat(stateOverride1.getBalance()).isEqualTo(Optional.of(Wei.fromHexString("0x01")));
assertTrue(stateOverride1.getStateDiff().isPresent());
assertThat(stateOverride1.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
assertTrue(stateOverride1.getState().isPresent());
assertThat(stateOverride1.getState().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
assertFalse(stateOverride1.getStateDiff().isPresent());
final StateOverride stateOverride2 =
stateOverrideParam.get(Address.fromHexString(ADDRESS_HEX2));
assertThat(stateOverride2.getNonce().get()).isEqualTo(157);
assertThat(stateOverride2.getBalance()).isEqualTo(Optional.of(Wei.fromHexString("0xFF")));
assertTrue(stateOverride2.getStateDiff().isPresent());
assertThat(stateOverride2.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
assertTrue(stateOverride2.getState().isPresent());
assertThat(stateOverride2.getState().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE);
assertFalse(stateOverride2.getStateDiff().isPresent());
}
private JsonRpcRequest readJsonAsJsonRpcRequest(final String json) throws java.io.IOException {
return new ObjectMapper().readValue(json, JsonRpcRequest.class);
}
@Test
public void shouldThrowExceptionWhenStateAndStateDiffAreBothPresent() {
Exception exception =
assertThrows(
IllegalStateException.class,
() ->
new StateOverride.Builder()
.withState(Map.of("0x1234", "0x5678"))
.withStateDiff(Map.of("0x1234", "0x5678"))
.build());
final String expectedMessage = "Cannot set both state and stateDiff";
assertThat(exception.getMessage()).isEqualTo(expectedMessage);
}
}

View File

@@ -73,7 +73,7 @@
"balance": "0x0",
"nonce": "0x1"
},
"0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC": {
"0x0000f90827f1c53a10cb7a02335b175320002935": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"balance": "0x0",
"nonce": "0x1"

View File

@@ -421,8 +421,8 @@ public abstract class PendingTransaction
* class changes its structure.
*/
public interface MemorySize {
int FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE = 904;
int EIP1559_AND_EIP4844_SHALLOW_SIZE = 1016;
int FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE = 912;
int EIP1559_AND_EIP4844_SHALLOW_SIZE = 1024;
int OPTIONAL_TO_SIZE = 112;
int OPTIONAL_CHAIN_ID_SIZE = 80;
int PAYLOAD_SHALLOW_SIZE = 32;

View File

@@ -42,12 +42,10 @@ import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
@@ -315,8 +313,6 @@ public class LayeredPendingTransactions implements PendingTransactions {
@Override
public void selectTransactions(final PendingTransactions.TransactionSelector selector) {
final Set<Address> skipSenders = new HashSet<>();
final Map<Byte, List<SenderPendingTransactions>> candidateTxsByScore;
synchronized (this) {
// since selecting transactions for block creation is a potential long operation
@@ -332,50 +328,39 @@ public class LayeredPendingTransactions implements PendingTransactions {
for (final var senderTxs : entry.getValue()) {
LOG.trace("Evaluating sender txs {}", senderTxs);
if (!skipSenders.contains(senderTxs.sender())) {
for (final var candidatePendingTx : senderTxs.pendingTransactions()) {
final var selectionResult = selector.evaluateTransaction(candidatePendingTx);
for (final var candidatePendingTx : senderTxs.pendingTransactions()) {
final var selectionResult = selector.evaluateTransaction(candidatePendingTx);
LOG.atTrace()
.setMessage("Selection result {} for transaction {}")
.addArgument(selectionResult)
.addArgument(candidatePendingTx::toTraceLog)
.log();
if (selectionResult.discard()) {
ethScheduler.scheduleTxWorkerTask(
() -> {
synchronized (this) {
prioritizedTransactions.remove(candidatePendingTx, INVALIDATED);
}
});
logDiscardedTransaction(candidatePendingTx, selectionResult);
} else if (selectionResult.penalize()) {
ethScheduler.scheduleTxWorkerTask(
() -> {
synchronized (this) {
prioritizedTransactions.penalize(candidatePendingTx);
}
});
LOG.atTrace()
.setMessage("Selection result {} for transaction {}")
.addArgument(selectionResult)
.setMessage("Transaction {} penalized")
.addArgument(candidatePendingTx::toTraceLog)
.log();
}
if (selectionResult.discard()) {
ethScheduler.scheduleTxWorkerTask(
() -> {
synchronized (this) {
prioritizedTransactions.remove(candidatePendingTx, INVALIDATED);
}
});
logDiscardedTransaction(candidatePendingTx, selectionResult);
} else if (selectionResult.penalize()) {
ethScheduler.scheduleTxWorkerTask(
() -> {
synchronized (this) {
prioritizedTransactions.penalize(candidatePendingTx);
}
});
LOG.atTrace()
.setMessage("Transaction {} penalized")
.addArgument(candidatePendingTx::toTraceLog)
.log();
}
if (selectionResult.stop()) {
LOG.trace("Stopping selection");
break selection;
}
if (!selectionResult.selected()) {
// avoid processing other txs from this sender if this one is skipped
// since the following will not be selected due to the nonce gap
LOG.trace("Skipping remaining txs for sender {}", candidatePendingTx.getSender());
skipSenders.add(candidatePendingTx.getSender());
break;
}
if (selectionResult.stop()) {
LOG.trace("Stopping selection");
break selection;
}
}
}

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.eth.transactions.layered;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.hyperledger.besu.datatypes.TransactionType.BLOB;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ADDED;
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ALREADY_KNOWN;
@@ -23,15 +24,13 @@ import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedRes
import static org.hyperledger.besu.ethereum.eth.transactions.layered.AddReason.MOVE;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.AddReason.NEW;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredRemovalReason.PoolRemovalReason.DROPPED;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredRemovalReason.PoolRemovalReason.INVALIDATED;
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredRemovalReason.PoolRemovalReason.REPLACED;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOB_PRICE_BELOW_CURRENT_MIN;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_FULL;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.CURRENT_TX_PRICE_BELOW_MIN;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_GAS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@@ -49,6 +48,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolCo
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionAddedListener;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactionDroppedListener;
import org.hyperledger.besu.ethereum.eth.transactions.RemovalReason;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolReplacementHandler;
@@ -57,10 +57,12 @@ import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.SequencedMap;
import java.util.function.BiFunction;
import java.util.stream.Stream;
@@ -479,46 +481,6 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
assertThat(iterationOrder).containsExactly(transaction0, transaction1, transaction2);
}
@ParameterizedTest
@MethodSource
public void ignoreSenderTransactionsAfterASkippedOne(
final TransactionSelectionResult skipSelectionResult) {
final Transaction transaction0a = createTransaction(0, DEFAULT_BASE_FEE.add(Wei.of(20)), KEYS1);
final Transaction transaction1a = createTransaction(1, DEFAULT_BASE_FEE.add(Wei.of(20)), KEYS1);
final Transaction transaction2a = createTransaction(2, DEFAULT_BASE_FEE.add(Wei.of(20)), KEYS1);
final Transaction transaction0b = createTransaction(0, DEFAULT_BASE_FEE.add(Wei.of(10)), KEYS2);
pendingTransactions.addTransaction(
createLocalPendingTransaction(transaction0a), Optional.empty());
pendingTransactions.addTransaction(
createLocalPendingTransaction(transaction1a), Optional.empty());
pendingTransactions.addTransaction(
createLocalPendingTransaction(transaction2a), Optional.empty());
pendingTransactions.addTransaction(
createLocalPendingTransaction(transaction0b), Optional.empty());
final List<Transaction> iterationOrder = new ArrayList<>(3);
pendingTransactions.selectTransactions(
pendingTx -> {
iterationOrder.add(pendingTx.getTransaction());
// pretending that the 2nd tx of the 1st sender is not selected
return pendingTx.getNonce() == 1 ? skipSelectionResult : SELECTED;
});
// the 3rd tx of the 1st must not be processed, since the 2nd is skipped
// but the 2nd sender must not be affected
assertThat(iterationOrder).containsExactly(transaction0a, transaction1a, transaction0b);
}
static Stream<TransactionSelectionResult> ignoreSenderTransactionsAfterASkippedOne() {
return Stream.of(
CURRENT_TX_PRICE_BELOW_MIN,
BLOB_PRICE_BELOW_CURRENT_MIN,
TX_TOO_LARGE_FOR_REMAINING_GAS,
TransactionSelectionResult.invalidTransient(GAS_PRICE_BELOW_CURRENT_BASE_FEE.name()),
TransactionSelectionResult.invalid(UPFRONT_COST_EXCEEDS_BALANCE.name()));
}
@Test
public void notForceNonceOrderWhenSendersDiffer() {
final Account sender2 = mock(Account.class);
@@ -547,9 +509,10 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
@Test
public void invalidTransactionIsDeletedFromPendingTransactions() {
final var pendingTx0 = createRemotePendingTransaction(transaction0);
final var pendingTx1 = createRemotePendingTransaction(transaction1);
pendingTransactions.addTransaction(pendingTx0, Optional.empty());
pendingTransactions.addTransaction(pendingTx1, Optional.empty());
final var droppedTxCollector = new DroppedTransactionCollector();
pendingTransactions.subscribeDroppedTransactions(droppedTxCollector);
final List<PendingTransaction> parsedTransactions = new ArrayList<>(1);
pendingTransactions.selectTransactions(
@@ -558,11 +521,11 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
return TransactionSelectionResult.invalid(UPFRONT_COST_EXCEEDS_BALANCE.name());
});
// only the first is processed since not being selected will automatically skip the processing
// all the other txs from the same sender
// assert that first tx is removed from the pool
assertThat(droppedTxCollector.droppedTransactions)
.containsExactly(entry(transaction0, INVALIDATED));
assertThat(parsedTransactions).containsExactly(pendingTx0);
assertThat(pendingTransactions.getPendingTransactions()).containsExactly(pendingTx1);
assertThat(pendingTransactions.getPendingTransactions()).isEmpty();
}
@Test
@@ -947,4 +910,13 @@ public class LayeredPendingTransactionsTest extends BaseTransactionPoolTest {
ReadyTransactions readyTransactions,
SparseTransactions sparseTransactions,
EvictCollectorLayer evictedCollector) {}
static class DroppedTransactionCollector implements PendingTransactionDroppedListener {
final SequencedMap<Transaction, RemovalReason> droppedTransactions = new LinkedHashMap<>();
@Override
public void onTransactionDropped(final Transaction transaction, final RemovalReason reason) {
droppedTransactions.put(transaction, reason);
}
}
}

View File

@@ -27,6 +27,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.CodeDelegation;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
@@ -348,10 +349,12 @@ public class T8nExecutor {
Blockchain blockchain = new T8nBlockchain(referenceTestEnv, protocolSpec);
final BlockHeader blockHeader = referenceTestEnv.parentBlockHeader(protocolSpec);
final MainnetTransactionProcessor processor = protocolSpec.getTransactionProcessor();
final Wei blobGasPrice =
protocolSpec
.getFeeMarket()
.blobGasPricePerGas(calculateExcessBlobGasForParent(protocolSpec, blockHeader));
final BlobGas excessBlobGas =
Optional.ofNullable(referenceTestEnv.getParentExcessBlobGas())
.map(
__ -> calculateExcessBlobGasForParent(protocolSpec, blockHeader)) // blockchain-test
.orElse(blockHeader.getExcessBlobGas().orElse(BlobGas.ZERO)); // state-test
final Wei blobGasPrice = protocolSpec.getFeeMarket().blobGasPricePerGas(excessBlobGas);
long blobGasLimit = protocolSpec.getGasLimitCalculator().currentBlobGasLimit();
if (!referenceTestEnv.isStateTest()) {

View File

@@ -27,8 +27,8 @@
"blobGasUsed": "0x00",
"excessBlobGas": "0x00",
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"requestsHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"hash": "0x67315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678"
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"hash": "0xf87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369"
},
"pre": {
"0x00000000219ab540356cbb839cbe05303d7705fa": {
@@ -178,7 +178,7 @@
"balance": "0x00",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"storage": {
"0x00": "0x67315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678"
"0x00": "0xf87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369"
}
},
"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
@@ -194,15 +194,15 @@
"storage": {}
}
},
"lastblockhash": "0x9ca58820df28ca6d09450fff5fdf93d39976e3aa098c6981ae08f391d44ffb3f",
"genesisRLP": "0xf90262f9025ba00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06cb1761e069313d13f39d755da011dc921b1f0fe5c4c3e951891639e479b4cfba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808088016345785d8a0000808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421c0c0c0c0",
"lastblockhash": "0x606a39bef40699cba8e82cac36274317a4a9f965db16e67121897e8144dd2ec6",
"genesisRLP": "0xf90262f9025ba00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06cb1761e069313d13f39d755da011dc921b1f0fe5c4c3e951891639e479b4cfba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808088016345785d8a0000808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855c0c0c0c0",
"blocks": [
{
"blockHeader": {
"parentHash": "0x67315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678",
"parentHash": "0xf87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369",
"uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"stateRoot": "0x61171b085ffd8d099ca59ba13164e8883d89c89d3298256aa229b03a6e33d246",
"stateRoot": "0x0dc49ca54f4370756d0f863551487c2bbc3a327891f9993b10d3985935bc1198",
"transactionsTrie": "0xec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3",
"receiptTrie": "0x9593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafa",
"bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
@@ -219,8 +219,8 @@
"blobGasUsed": "0x00",
"excessBlobGas": "0x00",
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"requestsHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"hash": "0x9ca58820df28ca6d09450fff5fdf93d39976e3aa098c6981ae08f391d44ffb3f"
"requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"hash": "0x606a39bef40699cba8e82cac36274317a4a9f965db16e67121897e8144dd2ec6"
},
"transactions": [
{
@@ -243,7 +243,7 @@
"depositRequests": [],
"withdrawalRequests": [],
"consolidationRequests": [],
"rlp": "0xf902c8f9025fa067315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa061171b085ffd8d099ca59ba13164e8883d89c89d3298256aa229b03a6e33d246a0ec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3a09593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a000082a8648203e800a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f862f860800a83989680940000000000000000000000000000000000001000808026a0e5d462429669f661291a8dc4c49a092cfd4922b6f3f31c9189a2f4adf5ecd730a001494afaf472fbb80bcb107ffeb918a2b9115f454027840615d6d20d63c69ac0c0c0",
"rlp": "0xf902c8f9025fa0f87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa00dc49ca54f4370756d0f863551487c2bbc3a327891f9993b10d3985935bc1198a0ec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3a09593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a000082a8648203e800a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855f862f860800a83989680940000000000000000000000000000000000001000808026a0e5d462429669f661291a8dc4c49a092cfd4922b6f3f31c9189a2f4adf5ecd730a001494afaf472fbb80bcb107ffeb918a2b9115f454027840615d6d20d63c69ac0c0c0",
"blocknumber": "1"
}
],
@@ -259,5 +259,5 @@
}
}
},
"stdout": "Considering tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\nBlock 1 (0x9ca58820df28ca6d09450fff5fdf93d39976e3aa098c6981ae08f391d44ffb3f) Imported\nChain import successful - tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\n"
"stdout": "Considering tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\nBlock 1 (0x606a39bef40699cba8e82cac36274317a4a9f965db16e67121897e8144dd2ec6) Imported\nChain import successful - tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\n"
}

View File

@@ -63,7 +63,7 @@
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
"storage": {}
},
"0x0f792be4b0c0cb4dae440ef133e90c0ecd48cccc": {
"0x0000f90827f1c53a10cb7a02335b175320002935": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"balance": "0x0",
"nonce": "0x1"
@@ -185,7 +185,7 @@
"balance": "0x0",
"nonce": "0x1"
},
"0x0f792be4b0c0cb4dae440ef133e90c0ecd48cccc": {
"0x0000f90827f1c53a10cb7a02335b175320002935": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0xe4fb5d47f70d54b4f36777ea4c882cf767f93d8f8170285d97a1b8275dfe4dbb"
@@ -204,7 +204,7 @@
"requests": [
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030100000000000000"
],
"stateRoot": "0xc769f83dbad9b87a209216d18c4b19cb12b61838594a2e8270898438f4e147af",
"stateRoot": "0x3db3e963227d69f80461436b60e4c9972a58d17f6219aa65b2ddfdf7da9f6604",
"txRoot": "0x2b790bf82ef7259a0e4513d1b89a77d81e99672ba68758ef2ba3fde32851d023",
"receiptsRoot": "0x9c8d7a917ecb3ff2566f264abbf39131e51b08b07eb2b69cb46989d79d985593",
"logsHash": "0x43e31613bfefc1f55d8b3ca2b61f933f3838d523dc11cb5d7ffdd2ecf0ab5d49",

View File

@@ -51,21 +51,21 @@
"0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7"
}
},
"0x0c15f14308530b7cdb8460094bbb9cc28b9aaaaa": {
"0x00000961ef480eb55e80d19ad83579a64c007002": {
"comment": "This is the runtime bytecode for the Withdrawal Request Smart Contract.",
"nonce": "0x01",
"balance": "0x00",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd",
"storage": {}
},
"0x00431f263ce400f4455c2dcf564e53007ca4bbbb": {
"0x0000bbddc7ce488642fb579f8b00f3a590007251": {
"comment": "Increase the MAX_EFFECTIVE_BALANCE",
"nonce": "0x01",
"balance": "0x00",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd",
"storage": {}
},
"0x0f792be4b0c0cb4dae440ef133e90c0ecd48cccc": {
"0x0000f90827f1c53a10cb7a02335b175320002935": {
"nonce": "0x01",
"balance": "0x00",
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
@@ -84,10 +84,10 @@
"storage": {}
},
"0x0000000000000000000000000000000000001000": {
"comment": "This is a proxy contract that calls 0c15f14308530b7cdb8460094bbb9cc28b9aaaaa ie Withdrawal Request",
"comment": "This is a proxy contract that calls 00000961ef480eb55e80d19ad83579a64c007002 ie Withdrawal Request",
"nonce": "0x01",
"balance": "0xad78ebc5ac62000000",
"code": "0x6038600060003760006000603860006001730c15f14308530b7cdb8460094bbb9cc28b9aaaaa620f4240f150",
"code": "0x60386000600037600060006038600060017300000961ef480eb55e80d19ad83579a64c007002620f4240f150",
"storage": {}
}
},
@@ -134,7 +134,7 @@
"stdout": {
"alloc": {
"0x0000000000000000000000000000000000001000": {
"code": "0x6038600060003760006000603860006001730c15f14308530b7cdb8460094bbb9cc28b9aaaaa620f4240f150",
"code": "0x60386000600037600060006038600060017300000961ef480eb55e80d19ad83579a64c007002620f4240f150",
"balance": "0xad78ebc5ac61ffffff",
"nonce": "0x1"
},
@@ -184,12 +184,12 @@
"balance": "0x0",
"nonce": "0x1"
},
"0x00431f263ce400f4455c2dcf564e53007ca4bbbb": {
"0x0000bbddc7ce488642fb579f8b00f3a590007251": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd",
"balance": "0x0",
"nonce": "0x1"
},
"0x0c15f14308530b7cdb8460094bbb9cc28b9aaaaa": {
"0x00000961ef480eb55e80d19ad83579a64c007002": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000001000",
@@ -198,7 +198,7 @@
"balance": "0x1",
"nonce": "0x1"
},
"0x0f792be4b0c0cb4dae440ef133e90c0ecd48cccc": {
"0x0000f90827f1c53a10cb7a02335b175320002935": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x10715cfbefdb8a0cb2f7d7ca5ee6d1ea65515ecb41cff0a22d1e11716a9d27fb"
@@ -217,20 +217,20 @@
"requests": [
"0x0100000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000"
],
"stateRoot": "0xc7b49e4aef4229962b94ec0a7c83a6fc0b4015f2573b9a85446ed434c823164e",
"stateRoot": "0x3c7e1f4b3379b0112968dad5c4abfa297b8ce53ca06ef66c332ed17f41d19922",
"txRoot": "0x0d36638e52999b7beafa00eb94f7ca23139774cd14229c011d0edc1fc66125c9",
"receiptsRoot": "0x2af83312a6aa55bd8f169e65eec48f92d6d6dc3398bc038d7ccfab5d9aa26b3f",
"logsHash": "0xac344ad50aad544ec284bf76ac9b939f93e00f8fe16097a151df14bde2065f83",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000",
"receiptsRoot": "0xb188279b8dd5998da7f54224968a83203b9e1496f54d9c0d572a8faddf5dc3f5",
"logsHash": "0xb30a2575f084f925bf94b37ba4b8c32b87e8380e261a1f407489d46455044dbd",
"logsBloom": "0x00000000008000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"receipts": [
{
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0x1e6d4",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000",
"logsBloom": "0x00000000008000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"logs": [
{
"address": "0x0c15f14308530b7cdb8460094bbb9cc28b9aaaaa",
"address": "0x00000961ef480eb55e80d19ad83579a64c007002",
"topics": [],
"data": "0x00000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000",
"blockNumber": 1,

View File

@@ -247,6 +247,10 @@ public class ReferenceTestEnv extends BlockHeader {
return blockHashes;
}
public String getParentExcessBlobGas() {
return parentExcessBlobGas;
}
public boolean isStateTest() {
return isStateTest;
}

View File

@@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.bytes.DelegatingBytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.assertj.core.api.SoftAssertions;
import org.hyperledger.besu.datatypes.Address;
@@ -252,7 +253,7 @@ public class GeneralStateReferenceTestTools {
if (!storageEntries.isEmpty()) {
accountJson.set("storage", storageJson);
}
worldStateJson.set(account.getAddress().orElse(Address.wrap(Bytes.EMPTY)).toHexString(), accountJson);
worldStateJson.set(account.getAddress().map(DelegatingBytes::toHexString).orElse(Bytes.EMPTY.toHexString()), accountJson);
});
LOG.error("Calculated world state: \n{}", worldStateJson.toPrettyString());
}

View File

@@ -36,7 +36,6 @@ dependencies {
implementation 'io.tmio:tuweni-units'
implementation 'com.google.guava:guava'
jmh project(':util')
testImplementation project(':testutil')

View File

@@ -31,7 +31,6 @@ import org.apache.tuweni.units.bigints.UInt256;
import org.apache.tuweni.units.bigints.UInt64;
abstract class AbstractRLPInput implements RLPInput {
private static final String errorMessageSuffix = " (at bytes %d-%d: %s%s[%s]%s%s)";
private final boolean lenient;

View File

@@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.rlp.util.RLPTestUtil;
import java.util.Random;
import org.apache.tuweni.bytes.Bytes;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
public class RLPTest {
@@ -176,7 +177,7 @@ public class RLPTest {
// ["0x02"]
// ]
Bytes validRlp = Bytes.fromHexString("c4c101c102");
RLP.validate(validRlp);
Assertions.assertThatCode(() -> RLP.validate(validRlp)).doesNotThrowAnyException();
}
private static Bytes h(final String hex) {

View File

@@ -57,7 +57,7 @@ public class BytesTrieSet<E extends Bytes> extends AbstractSet<E> {
if (leafObject == null) sb.append("null");
else {
sb.append('[');
System.out.println(leafObject.toHexString());
sb.append(leafObject.toHexString());
sb.append(']');
}
sb.append(", children=");

View File

@@ -62,7 +62,7 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
/**
* The blob gas cost per blob. This is the gas cost for each blob of data that is added to the
* block.
* block. 1 << 17 = 131072 = 0x20000
*/
private static final long BLOB_GAS_PER_BLOB = 1 << 17;

View File

@@ -1,77 +0,0 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operation;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.CodeDelegationHelper;
import org.apache.tuweni.bytes.Bytes;
/**
* ExtCode* operations treat EOAs with delegated code differently than other operations. This
* abstract class contains common methods for this behaviour.
*/
abstract class AbstractExtCodeOperation extends AbstractOperation {
/**
* Instantiates a new Abstract operation.
*
* @param opcode the opcode
* @param name the name
* @param stackItemsConsumed the stack items consumed
* @param stackItemsProduced the stack items produced
* @param gasCalculator the gas calculator
*/
protected AbstractExtCodeOperation(
final int opcode,
final String name,
final int stackItemsConsumed,
final int stackItemsProduced,
final GasCalculator gasCalculator) {
super(opcode, name, stackItemsConsumed, stackItemsProduced, gasCalculator);
}
/**
* Returns the code for standard accounts or a special designator for EOAs with delegated code
*
* @param account The account
* @return the code or the special 7702 designator
*/
protected Bytes getCode(final Account account) {
if (account == null) {
return Bytes.EMPTY;
}
return account.hasDelegatedCode()
? CodeDelegationHelper.getCodeDelegationForRead()
: account.getCode();
}
/**
* Returns the code hash for standard accounts or a special designator for EOAs with delegated
* code
*
* @param account The account
* @return the code hash or the hash of the special 7702 designator
*/
protected Hash getCodeHash(final Account account) {
if (account.hasDelegatedCode()) {
return Hash.hash(CodeDelegationHelper.getCodeDelegationForRead());
}
return account.getCodeHash();
}
}

View File

@@ -57,9 +57,9 @@ public class BlockHashOperation extends AbstractOperation {
final long currentBlockNumber = blockValues.getNumber();
final BlockHashLookup blockHashLookup = frame.getBlockHashLookup();
// If the current block is the genesis block or the sought block is
// not within the lookback window, zero is returned.
if (currentBlockNumber == 0
// If the sought block is negative, a future block, the current block, or not in the
// lookback window, zero is returned.
if (soughtBlock < 0
|| soughtBlock >= currentBlockNumber
|| soughtBlock < (currentBlockNumber - blockHashLookup.getLookback())) {
frame.pushStackItem(Bytes32.ZERO);

View File

@@ -29,7 +29,7 @@ import org.hyperledger.besu.evm.internal.Words;
import org.apache.tuweni.bytes.Bytes;
/** The Ext code copy operation. */
public class ExtCodeCopyOperation extends AbstractExtCodeOperation {
public class ExtCodeCopyOperation extends AbstractOperation {
/** This is the "code" legacy contracts see when copying code from an EOF contract. */
public static final Bytes EOF_REPLACEMENT_CODE = Bytes.fromHexString("0xef00");
@@ -93,8 +93,7 @@ public class ExtCodeCopyOperation extends AbstractExtCodeOperation {
}
final Account account = frame.getWorldUpdater().get(address);
final Bytes code = getCode(account);
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
if (enableEIP3540
&& code.size() >= 2

View File

@@ -29,7 +29,7 @@ import org.hyperledger.besu.evm.internal.Words;
import org.apache.tuweni.bytes.Bytes;
/** The Ext code hash operation. */
public class ExtCodeHashOperation extends AbstractExtCodeOperation {
public class ExtCodeHashOperation extends AbstractOperation {
// // 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5
static final Hash EOF_REPLACEMENT_HASH = Hash.hash(ExtCodeCopyOperation.EOF_REPLACEMENT_CODE);
@@ -85,14 +85,14 @@ public class ExtCodeHashOperation extends AbstractExtCodeOperation {
if (account == null || account.isEmpty()) {
frame.pushStackItem(Bytes.EMPTY);
} else {
final Bytes code = getCode(account);
final Bytes code = account.getCode();
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
frame.pushStackItem(EOF_REPLACEMENT_HASH);
} else {
frame.pushStackItem(getCodeHash(account));
frame.pushStackItem(account.getCodeHash());
}
}
return new OperationResult(cost, null);

View File

@@ -28,7 +28,7 @@ import org.hyperledger.besu.evm.internal.Words;
import org.apache.tuweni.bytes.Bytes;
/** The Ext code size operation. */
public class ExtCodeSizeOperation extends AbstractExtCodeOperation {
public class ExtCodeSizeOperation extends AbstractOperation {
static final Bytes EOF_SIZE = Bytes.of(2);
@@ -83,7 +83,7 @@ public class ExtCodeSizeOperation extends AbstractExtCodeOperation {
if (account == null) {
codeSize = Bytes.EMPTY;
} else {
final Bytes code = getCode(account);
final Bytes code = account.getCode();
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE

Some files were not shown because too many files have changed in this diff Show More