mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 13:58:02 -05:00
Merge branch 'main' into zkbesu
# Conflicts: # .github/pull_request_template.md # .github/workflows/sonarcloud.yml
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -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
|
||||
|
||||
|
||||
@@ -532,6 +532,7 @@ public class BesuNodeFactory {
|
||||
.jsonRpcTxPool()
|
||||
.engineRpcEnabled(true)
|
||||
.jsonRpcDebug()
|
||||
.dataStorageConfiguration(DataStorageConfiguration.DEFAULT_BONSAI_CONFIG)
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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="
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<>());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"method": "debug_traceCall",
|
||||
"params": [
|
||||
{
|
||||
"to": "0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC",
|
||||
"to": "0x0000f90827f1c53a10cb7a02335b175320002935",
|
||||
"data": "0x000000000000000000000000000000000000000000000000000000000001A00E"
|
||||
},
|
||||
"latest",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -52,6 +52,7 @@
|
||||
"type": "0x3",
|
||||
"value": "0x0",
|
||||
"yParity": "0x0",
|
||||
"v" : "0x0",
|
||||
"r": "0x6ae0612cfda43a9b464b10b4881c6fc2e4c24533cf89bbe07934da65c3ae49ce",
|
||||
"s": "0x125387aeb222ec51130cf99cbdabf24bd4a881914faed69f254e4a3f4bc507fc",
|
||||
"blobVersionedHashes": [
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"type": "0x3",
|
||||
"value": "0x0",
|
||||
"yParity": "0x0",
|
||||
"v" : "0x0",
|
||||
"r": "0x6ae0612cfda43a9b464b10b4881c6fc2e4c24533cf89bbe07934da65c3ae49ce",
|
||||
"s": "0x125387aeb222ec51130cf99cbdabf24bd4a881914faed69f254e4a3f4bc507fc",
|
||||
"blobVersionedHashes": [
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"type": "0x3",
|
||||
"value": "0x0",
|
||||
"yParity": "0x0",
|
||||
"v" : "0x0",
|
||||
"r": "0x6ae0612cfda43a9b464b10b4881c6fc2e4c24533cf89bbe07934da65c3ae49ce",
|
||||
"s": "0x125387aeb222ec51130cf99cbdabf24bd4a881914faed69f254e4a3f4bc507fc",
|
||||
"blobVersionedHashes": [
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,7 +27,7 @@ public class CodeDelegationResult {
|
||||
accessedDelegatorAddresses.add(address);
|
||||
}
|
||||
|
||||
public void incremenentAlreadyExistingDelegators() {
|
||||
public void incrementAlreadyExistingDelegators() {
|
||||
alreadyExistingDelegators += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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[][] {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"balance": "0x0",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC": {
|
||||
"0x0000f90827f1c53a10cb7a02335b175320002935": {
|
||||
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
|
||||
"balance": "0x0",
|
||||
"nonce": "0x1"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -247,6 +247,10 @@ public class ReferenceTestEnv extends BlockHeader {
|
||||
return blockHashes;
|
||||
}
|
||||
|
||||
public String getParentExcessBlobGas() {
|
||||
return parentExcessBlobGas;
|
||||
}
|
||||
|
||||
public boolean isStateTest() {
|
||||
return isStateTest;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ dependencies {
|
||||
implementation 'io.tmio:tuweni-units'
|
||||
implementation 'com.google.guava:guava'
|
||||
|
||||
|
||||
jmh project(':util')
|
||||
|
||||
testImplementation project(':testutil')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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=");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user