Merge branch 'main' into zkbesu

# Conflicts:
#	plugin-api/build.gradle
This commit is contained in:
Fabio Di Fabio
2024-07-26 10:53:52 +02:00
69 changed files with 1272 additions and 263 deletions

View File

@@ -11,6 +11,7 @@
### Breaking Changes
- Remove deprecated sync modes (X_SNAP and X_CHECKPOINT). Use SNAP and CHECKPOINT instead [#7309](https://github.com/hyperledger/besu/pull/7309)
- Remove PKI-backed QBFT (deprecated in 24.5.1) Other forms of QBFT remain unchanged. [#7293](https://github.com/hyperledger/besu/pull/7293)
- Do not maintain connections to PoA bootnodes [#7358](https://github.com/hyperledger/besu/pull/7358). See [#7314](https://github.com/hyperledger/besu/pull/7314) for recommended alternative behaviour.
### Additions and Improvements
- `--Xsnapsync-bft-enabled` option enables experimental support for snap sync with IBFT/QBFT permissioned Bonsai-DB chains [#7140](https://github.com/hyperledger/besu/pull/7140)
@@ -23,12 +24,16 @@
- Added EIP-7702 [#7237](https://github.com/hyperledger/besu/pull/7237)
- Implement gnark-crypto for eip-196 [#7262](https://github.com/hyperledger/besu/pull/7262)
- Add trie log pruner metrics [#7352](https://github.com/hyperledger/besu/pull/7352)
- Force bonsai-limit-trie-logs-enabled=false when sync-mode=FULL instead of startup error [#7357](https://github.com/hyperledger/besu/pull/7357)
- `--Xbonsai-parallel-tx-processing-enabled` option enables executing transactions in parallel during block processing for Bonsai nodes
- Reduce default trie log pruning window size from 30,000 to 5,000 [#7365](https://github.com/hyperledger/besu/pull/7365)
- Add option `--poa-discovery-retry-bootnodes` for PoA networks to always use bootnodes during peer refresh, not just on first start [#7314](https://github.com/hyperledger/besu/pull/7314)
### Bug fixes
- Fix `eth_call` deserialization to correctly ignore unknown fields in the transaction object. [#7323](https://github.com/hyperledger/besu/pull/7323)
- Prevent Besu from starting up with sync-mode=FULL and bonsai-limit-trie-logs-enabled=true for private networks [#7357](https://github.com/hyperledger/besu/pull/7357)
- Add 30 second timeout to trie log pruner preload [#7365](https://github.com/hyperledger/besu/pull/7365)
- Avoid executing pruner preload during trie log subcommands [#7366](https://github.com/hyperledger/besu/pull/7366)
## 24.7.0

View File

@@ -821,20 +821,7 @@ public class RunnerBuilder {
LOG.debug("added ethash observer: {}", stratumServer.get());
}
final Stream<EnodeURL> maintainedPeers;
if (besuController.getGenesisConfigOptions().isPoa()) {
// In a permissioned chain Besu should maintain connections to both static nodes and
// bootnodes, which includes retries periodically
maintainedPeers =
sanitizePeers(
network,
Stream.concat(staticNodes.stream(), bootnodes.stream()).collect(Collectors.toList()));
LOG.debug("Added bootnodes to the maintained peer list");
} else {
// In a public chain only maintain connections to static nodes
maintainedPeers = sanitizePeers(network, staticNodes);
}
maintainedPeers
sanitizePeers(network, staticNodes)
.map(DefaultPeer::fromEnodeURL)
.forEach(peerNetwork::addMaintainedConnectionPeer);

View File

@@ -141,6 +141,7 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;
import org.hyperledger.besu.evm.precompile.AbstractAltBnPrecompiledContract;
import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract;
import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract;
@@ -1601,7 +1602,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private void validateDataStorageOptions() {
dataStorageOptions.validate(commandLine, syncMode);
dataStorageOptions.validate(commandLine);
}
private void validateRequiredOptions() {
@@ -2261,10 +2262,41 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return miningParameters;
}
private DataStorageConfiguration getDataStorageConfiguration() {
/**
* Get the data storage configuration
*
* @return the data storage configuration
*/
public DataStorageConfiguration getDataStorageConfiguration() {
if (dataStorageConfiguration == null) {
dataStorageConfiguration = dataStorageOptions.toDomainObject();
}
if (SyncMode.FULL.equals(getDefaultSyncModeIfNotSet())
&& DataStorageFormat.BONSAI.equals(dataStorageConfiguration.getDataStorageFormat())
&& dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled()) {
if (CommandLineUtils.isOptionSet(
commandLine, DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED)) {
throw new ParameterException(
commandLine,
String.format(
"Cannot enable %s with --sync-mode=%s and --data-storage-format=%s. You must set %s or use a different sync-mode",
DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED,
SyncMode.FULL,
DataStorageFormat.BONSAI,
DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false"));
}
dataStorageConfiguration =
ImmutableDataStorageConfiguration.copyOf(dataStorageConfiguration)
.withBonsaiLimitTrieLogsEnabled(false);
logger.warn(
"Forcing {}, since it cannot be enabled with --sync-mode={} and --data-storage-format={}.",
DataStorageOptions.BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false",
SyncMode.FULL,
DataStorageFormat.BONSAI);
}
return dataStorageConfiguration;
}

View File

@@ -24,7 +24,6 @@ import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.
import org.hyperledger.besu.cli.options.CLIOptions;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.ethereum.eth.sync.SyncMode;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
@@ -63,7 +62,8 @@ public class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
arity = "1")
private Long bonsaiMaxLayersToLoad = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;
private static final String BONSAI_LIMIT_TRIE_LOGS_ENABLED = "--bonsai-limit-trie-logs-enabled";
/** The bonsai limit trie logs enabled option name */
public static final String BONSAI_LIMIT_TRIE_LOGS_ENABLED = "--bonsai-limit-trie-logs-enabled";
/** The bonsai trie logs pruning window size. */
public static final String BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE =
@@ -147,20 +147,10 @@ public class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
* Validates the data storage options
*
* @param commandLine the full commandLine to check all the options specified by the user
* @param syncMode the sync mode
*/
public void validate(final CommandLine commandLine, final SyncMode syncMode) {
public void validate(final CommandLine commandLine) {
if (DataStorageFormat.BONSAI == dataStorageFormat) {
if (bonsaiLimitTrieLogsEnabled) {
if (SyncMode.FULL == syncMode) {
throw new CommandLine.ParameterException(
commandLine,
String.format(
"Cannot enable %s with sync-mode %s. You must set %s or use a different sync-mode",
BONSAI_LIMIT_TRIE_LOGS_ENABLED,
SyncMode.FULL,
BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false"));
}
if (bonsaiMaxLayersToLoad < MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT) {
throw new CommandLine.ParameterException(
commandLine,

View File

@@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogPruner;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
import java.io.IOException;
@@ -82,7 +83,14 @@ public class TrieLogSubCommand implements Runnable {
}
private static BesuController createBesuController() {
return parentCommand.besuCommand.buildController();
final DataStorageConfiguration config = parentCommand.besuCommand.getDataStorageConfiguration();
// disable limit trie logs to avoid preloading during subcommand execution
return parentCommand
.besuCommand
.getControllerBuilder()
.dataStorageConfiguration(
ImmutableDataStorageConfiguration.copyOf(config).withBonsaiLimitTrieLogsEnabled(false))
.build();
}
@Command(

View File

@@ -65,6 +65,7 @@ import org.hyperledger.besu.evm.precompile.KZGPointEvalPrecompiledContract;
import org.hyperledger.besu.metrics.StandardMetricCategory;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage;
import org.hyperledger.besu.util.number.PositiveNumber;
@@ -1303,13 +1304,34 @@ public class BesuCommandTest extends CommandTestAbstract {
}
@Test
public void parsesInvalidDefaultBonsaiLimitTrieLogsWhenFullSyncEnabled() {
public void bonsaiLimitTrieLogsDisabledWhenFullSyncEnabled() {
parseCommand("--sync-mode=FULL");
verify(mockControllerBuilder)
.dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture());
final DataStorageConfiguration dataStorageConfiguration =
dataStorageConfigurationArgumentCaptor.getValue();
assertThat(dataStorageConfiguration.getDataStorageFormat()).isEqualTo(BONSAI);
assertThat(dataStorageConfiguration.getBonsaiLimitTrieLogsEnabled()).isFalse();
verify(mockLogger)
.warn(
"Forcing {}, since it cannot be enabled with --sync-mode={} and --data-storage-format={}.",
"--bonsai-limit-trie-logs-enabled=false",
SyncMode.FULL,
DataStorageFormat.BONSAI);
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void parsesInvalidWhenFullSyncAndBonsaiLimitTrieLogsExplicitlyTrue() {
parseCommand("--sync-mode=FULL", "--bonsai-limit-trie-logs-enabled=true");
Mockito.verifyNoInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8))
.contains("Cannot enable --bonsai-limit-trie-logs-enabled with sync-mode FULL");
.contains(
"Cannot enable --bonsai-limit-trie-logs-enabled with --sync-mode=FULL and --data-storage-format=BONSAI. You must set --bonsai-limit-trie-logs-enabled=false or use a different sync-mode");
}
@Test

View File

@@ -55,14 +55,6 @@ public class DataStorageOptionsTest
"--bonsai-limit-trie-logs-enabled=false");
}
@Test
public void bonsaiTrieLogPruningWindowSizeShouldBePositive2() {
internalTestFailure(
"Cannot enable --bonsai-limit-trie-logs-enabled with sync-mode FULL. You must set --bonsai-limit-trie-logs-enabled=false or use a different sync-mode",
"--sync-mode",
"FULL");
}
@Test
public void bonsaiTrieLogPruningWindowSizeShouldBePositive() {
internalTestFailure(

View File

@@ -0,0 +1,74 @@
/*
* 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.cli.subcommands.storage;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.cli.CommandTestAbstract;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import java.util.List;
import org.junit.jupiter.api.Test;
class TrieLogSubCommandTest extends CommandTestAbstract {
@Test
void limitTrieLogsDefaultDisabledForAllSubcommands() {
assertTrieLogSubcommand("prune");
assertTrieLogSubcommand("count");
assertTrieLogSubcommand("import");
assertTrieLogSubcommand("export");
}
@Test
void limitTrieLogsDisabledForAllSubcommands() {
assertTrieLogSubcommandWithExplicitLimitEnabled("prune");
assertTrieLogSubcommandWithExplicitLimitEnabled("count");
assertTrieLogSubcommandWithExplicitLimitEnabled("import");
assertTrieLogSubcommandWithExplicitLimitEnabled("export");
}
private void assertTrieLogSubcommand(final String trieLogSubcommand) {
parseCommand("storage", "trie-log", trieLogSubcommand);
assertConfigurationIsDisabledBySubcommand();
}
private void assertTrieLogSubcommandWithExplicitLimitEnabled(final String trieLogSubcommand) {
parseCommand("--bonsai-limit-trie-logs-enabled=true", "storage", "trie-log", trieLogSubcommand);
assertConfigurationIsDisabledBySubcommand();
}
private void assertConfigurationIsDisabledBySubcommand() {
verify(mockControllerBuilder, atLeastOnce())
.dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture());
final List<DataStorageConfiguration> configs =
dataStorageConfigurationArgumentCaptor.getAllValues();
assertThat(configs.get(0).getBonsaiLimitTrieLogsEnabled()).isTrue();
assertThat(configs.get(1).getBonsaiLimitTrieLogsEnabled()).isFalse();
}
@Test
void limitTrieLogsDefaultEnabledForBesuMainCommand() {
parseCommand();
verify(mockControllerBuilder, atLeastOnce())
.dataStorageConfiguration(dataStorageConfigurationArgumentCaptor.capture());
final List<DataStorageConfiguration> configs =
dataStorageConfigurationArgumentCaptor.getAllValues();
assertThat(configs).allMatch(DataStorageConfiguration::getBonsaiLimitTrieLogsEnabled);
}
}

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.blockcreation.txselection;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.INVALID_TX_EVALUATION_TOO_LONG;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG;
@@ -419,11 +420,14 @@ public class BlockTransactionSelector {
final var pendingTransaction = evaluationContext.getPendingTransaction();
// check if this tx took too much to evaluate, and in case remove it from the pool
// check if this tx took too much to evaluate, and in case it was invalid remove it from the
// pool, otherwise penalize it.
final TransactionSelectionResult actualResult =
isTimeout.get()
? transactionTookTooLong(evaluationContext)
? TX_EVALUATION_TOO_LONG
? transactionTookTooLong(evaluationContext, selectionResult)
? selectionResult.discard()
? INVALID_TX_EVALUATION_TOO_LONG
: TX_EVALUATION_TOO_LONG
: BLOCK_SELECTION_TIMEOUT
: selectionResult;
@@ -441,16 +445,21 @@ public class BlockTransactionSelector {
return actualResult;
}
private boolean transactionTookTooLong(final TransactionEvaluationContext evaluationContext) {
private boolean transactionTookTooLong(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult selectionResult) {
final var evaluationTimer = evaluationContext.getEvaluationTimer();
if (evaluationTimer.elapsed(TimeUnit.MILLISECONDS) > blockTxsSelectionMaxTime) {
LOG.atWarn()
.setMessage(
"Transaction {} is too late for inclusion, evaluated in {} that is over the max limit of {}ms"
+ ", removing it from the pool")
"Transaction {} is too late for inclusion, with result {}, evaluated in {} that is over the max limit of {}ms"
+ ", {}")
.addArgument(evaluationContext.getPendingTransaction()::getHash)
.addArgument(selectionResult)
.addArgument(evaluationTimer)
.addArgument(blockTxsSelectionMaxTime)
.addArgument(
selectionResult.discard() ? "removing it from the pool" : "penalizing it in the pool")
.log();
return true;
}

View File

@@ -110,7 +110,8 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele
* @return True if the invalid reason is transient, false otherwise.
*/
private boolean isTransientValidationError(final TransactionInvalidReason invalidReason) {
return invalidReason.equals(TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE)
return invalidReason.equals(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE)
|| invalidReason.equals(TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE)
|| invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH);
}
}

View File

@@ -18,8 +18,9 @@ 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.core.MiningParameters.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_LOW;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.INVALID_TX_EVALUATION_TOO_LONG;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG;
@@ -296,7 +297,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Transaction tx = createTransaction(i, Wei.of(7), 100_000);
transactionsToInject.add(tx);
if (i == 1) {
ensureTransactionIsInvalid(tx, TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE);
ensureTransactionIsInvalid(tx, TransactionInvalidReason.NONCE_TOO_LOW);
} else {
ensureTransactionIsValid(tx);
}
@@ -311,8 +312,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
.containsOnly(
entry(
invalidTx,
TransactionSelectionResult.invalid(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE.name())));
TransactionSelectionResult.invalid(TransactionInvalidReason.NONCE_TOO_LOW.name())));
assertThat(results.getSelectedTransactions().size()).isEqualTo(4);
assertThat(results.getSelectedTransactions().contains(invalidTx)).isFalse();
assertThat(results.getReceipts().size()).isEqualTo(4);
@@ -568,8 +568,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
ensureTransactionIsValid(validTransaction, 21_000, 0);
final Transaction invalidTransaction = createTransaction(3, Wei.of(10), 21_000);
ensureTransactionIsInvalid(
invalidTransaction, TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE);
ensureTransactionIsInvalid(invalidTransaction, TransactionInvalidReason.NONCE_TOO_LOW);
transactionPool.addRemoteTransactions(List.of(validTransaction, invalidTransaction));
@@ -582,8 +581,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
.containsOnly(
entry(
invalidTransaction,
TransactionSelectionResult.invalid(
TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE.name())));
TransactionSelectionResult.invalid(TransactionInvalidReason.NONCE_TOO_LOW.name())));
}
@Test
@@ -948,7 +946,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
@ParameterizedTest
@MethodSource("subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver")
public void pendingTransactionsThatTakesTooLongToEvaluateIsDroppedFromThePool(
public void pendingTransactionsThatTakesTooLongToEvaluateIsPenalized(
final boolean isPoa,
final boolean preProcessingTooLate,
final boolean processingTooLate,
@@ -961,7 +959,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
postProcessingTooLate,
900,
TX_EVALUATION_TOO_LONG,
true);
false);
}
private void internalBlockSelectionTimeoutSimulation(
@@ -1085,7 +1083,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
500,
BLOCK_SELECTION_TIMEOUT,
false,
UPFRONT_COST_EXCEEDS_BALANCE);
NONCE_TOO_LOW);
}
@ParameterizedTest
@@ -1102,9 +1100,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
processingTooLate,
postProcessingTooLate,
900,
TX_EVALUATION_TOO_LONG,
INVALID_TX_EVALUATION_TOO_LONG,
true,
UPFRONT_COST_EXCEEDS_BALANCE);
NONCE_TOO_LOW);
}
private void internalBlockSelectionTimeoutSimulationInvalidTxs(
@@ -1423,15 +1421,17 @@ public abstract class AbstractBlockTransactionSelectorTest {
private static class PluginTransactionSelectionResult extends TransactionSelectionResult {
private enum PluginStatus implements Status {
PLUGIN_INVALID(false, true),
PLUGIN_INVALID_TRANSIENT(false, false);
PLUGIN_INVALID(false, true, false),
PLUGIN_INVALID_TRANSIENT(false, false, true);
private final boolean stop;
private final boolean discard;
private final boolean penalize;
PluginStatus(final boolean stop, final boolean discard) {
PluginStatus(final boolean stop, final boolean discard, final boolean penalize) {
this.stop = stop;
this.discard = discard;
this.penalize = penalize;
}
@Override
@@ -1443,6 +1443,11 @@ public abstract class AbstractBlockTransactionSelectorTest {
public boolean discard() {
return discard;
}
@Override
public boolean penalize() {
return penalize;
}
}
public static final TransactionSelectionResult GENERIC_PLUGIN_INVALID_TRANSIENT =

View File

@@ -26,6 +26,12 @@ import org.hyperledger.besu.plugin.services.trielogs.TrieLogEvent;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -40,6 +46,7 @@ import org.slf4j.LoggerFactory;
public class TrieLogPruner implements TrieLogEvent.TrieLogObserver {
private static final Logger LOG = LoggerFactory.getLogger(TrieLogPruner.class);
private static final int PRELOAD_TIMEOUT_IN_SECONDS = 30;
private final int pruningLimit;
private final int loadingLimit;
@@ -83,25 +90,60 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver {
BesuMetricCategory.PRUNER, "trie_log_pruned_orphan", "trie log pruned orphan");
}
public int initialize() {
return preloadQueue();
public void initialize() {
preloadQueueWithTimeout();
}
private int preloadQueue() {
private void preloadQueueWithTimeout() {
LOG.atInfo()
.setMessage("Loading first {} trie logs from database...")
.setMessage("Attempting to load first {} trie logs from database...")
.addArgument(loadingLimit)
.log();
try (final ScheduledExecutorService preloadExecutor = Executors.newScheduledThreadPool(1)) {
final AtomicBoolean timeoutOccurred = new AtomicBoolean(false);
final Runnable timeoutTask =
() -> {
timeoutOccurred.set(true);
LOG.atWarn()
.setMessage(
"Timeout occurred while loading and processing {} trie logs from database")
.addArgument(loadingLimit)
.log();
};
final ScheduledFuture<?> timeoutFuture =
preloadExecutor.schedule(timeoutTask, PRELOAD_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
LOG.atInfo()
.setMessage(
"Trie log pruning will timeout after {} seconds. If this is timing out, consider using `besu storage trie-log prune` subcommand, see https://besu.hyperledger.org/public-networks/how-to/bonsai-limit-trie-logs")
.addArgument(PRELOAD_TIMEOUT_IN_SECONDS)
.log();
preloadQueue(timeoutOccurred, timeoutFuture);
}
}
private void preloadQueue(
final AtomicBoolean timeoutOccurred, final ScheduledFuture<?> timeoutFuture) {
try (final Stream<byte[]> trieLogKeys = rootWorldStateStorage.streamTrieLogKeys(loadingLimit)) {
final AtomicLong count = new AtomicLong();
final AtomicLong addToPruneQueueCount = new AtomicLong();
final AtomicLong orphansPruned = new AtomicLong();
trieLogKeys.forEach(
blockHashAsBytes -> {
if (timeoutOccurred.get()) {
throw new RuntimeException(
new TimeoutException("Timeout occurred while preloading trie log prune queue"));
}
final Hash blockHash = Hash.wrap(Bytes32.wrap(blockHashAsBytes));
final Optional<BlockHeader> header = blockchain.getBlockHeader(blockHash);
if (header.isPresent()) {
addToPruneQueue(header.get().getNumber(), blockHash);
count.getAndIncrement();
addToPruneQueueCount.getAndIncrement();
} else {
// prune orphaned blocks (sometimes created during block production)
rootWorldStateStorage.pruneTrieLog(blockHash);
@@ -109,12 +151,21 @@ public class TrieLogPruner implements TrieLogEvent.TrieLogObserver {
prunedOrphanCounter.inc();
}
});
timeoutFuture.cancel(true);
LOG.atDebug().log("Pruned {} orphaned trie logs from database...", orphansPruned.intValue());
LOG.atInfo().log("Loaded {} trie logs from database", count);
return pruneFromQueue() + orphansPruned.intValue();
LOG.atInfo().log(
"Added {} trie logs to prune queue. Commencing pruning of eligible trie logs...",
addToPruneQueueCount.intValue());
int prunedCount = pruneFromQueue();
LOG.atInfo().log("Pruned {} trie logs.", prunedCount);
} catch (Exception e) {
LOG.error("Error loading trie logs from database, nothing pruned", e);
return 0;
if (e.getCause() != null && e.getCause() instanceof TimeoutException) {
int prunedCount = pruneFromQueue();
LOG.atInfo().log("Operation timed out, but still pruned {} trie logs.", prunedCount);
} else {
LOG.error("Error loading trie logs from database, nothing pruned", e);
}
}
}

View File

@@ -25,7 +25,7 @@ public interface DataStorageConfiguration {
long DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD = 512;
boolean DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED = true;
long MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT = DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD;
int DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = 30_000;
int DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE = 5_000;
boolean DEFAULT_RECEIPT_COMPACTION_ENABLED = false;
DataStorageConfiguration DEFAULT_CONFIG =

View File

@@ -50,18 +50,20 @@ public abstract class PendingTransaction
private final Transaction transaction;
private final long addedAt;
private final long sequence; // Allows prioritization based on order transactions are added
private volatile byte score;
private int memorySize = NOT_INITIALIZED;
private PendingTransaction(
final Transaction transaction, final long addedAt, final long sequence) {
final Transaction transaction, final long addedAt, final long sequence, final byte score) {
this.transaction = transaction;
this.addedAt = addedAt;
this.sequence = sequence;
this.score = score;
}
private PendingTransaction(final Transaction transaction, final long addedAt) {
this(transaction, addedAt, TRANSACTIONS_ADDED.getAndIncrement());
this(transaction, addedAt, TRANSACTIONS_ADDED.getAndIncrement(), Byte.MAX_VALUE);
}
public static PendingTransaction newPendingTransaction(
@@ -123,6 +125,20 @@ public abstract class PendingTransaction
return memorySize;
}
public byte getScore() {
return score;
}
public void decrementScore() {
// use temp var to avoid non-atomic update of volatile var
final byte newScore = (byte) (score - 1);
// check to avoid underflow
if (newScore < score) {
score = newScore;
}
}
public abstract PendingTransaction detachedCopy();
private int computeMemorySize() {
@@ -255,6 +271,8 @@ public abstract class PendingTransaction
+ isReceivedFromLocalSource()
+ ", hasPriority="
+ hasPriority()
+ ", score="
+ score
+ '}';
}
@@ -267,6 +285,8 @@ public abstract class PendingTransaction
+ isReceivedFromLocalSource()
+ ", hasPriority="
+ hasPriority()
+ ", score="
+ score
+ ", "
+ transaction.toTraceLog()
+ "}";
@@ -282,13 +302,13 @@ public abstract class PendingTransaction
this(transaction, System.currentTimeMillis());
}
private Local(final long sequence, final Transaction transaction) {
super(transaction, System.currentTimeMillis(), sequence);
private Local(final long sequence, final byte score, final Transaction transaction) {
super(transaction, System.currentTimeMillis(), sequence, score);
}
@Override
public PendingTransaction detachedCopy() {
return new Local(getSequence(), getTransaction().detachedCopy());
return new Local(getSequence(), getScore(), getTransaction().detachedCopy());
}
@Override
@@ -310,13 +330,13 @@ public abstract class PendingTransaction
super(transaction, addedAt);
}
public Priority(final long sequence, final Transaction transaction) {
super(sequence, transaction);
public Priority(final long sequence, final byte score, final Transaction transaction) {
super(sequence, score, transaction);
}
@Override
public PendingTransaction detachedCopy() {
return new Priority(getSequence(), getTransaction().detachedCopy());
return new Priority(getSequence(), getScore(), getTransaction().detachedCopy());
}
@Override
@@ -336,13 +356,13 @@ public abstract class PendingTransaction
this(transaction, System.currentTimeMillis());
}
private Remote(final long sequence, final Transaction transaction) {
super(transaction, System.currentTimeMillis(), sequence);
private Remote(final long sequence, final byte score, final Transaction transaction) {
super(transaction, System.currentTimeMillis(), sequence, score);
}
@Override
public PendingTransaction detachedCopy() {
return new Remote(getSequence(), getTransaction().detachedCopy());
return new Remote(getSequence(), getScore(), getTransaction().detachedCopy());
}
@Override
@@ -364,13 +384,13 @@ public abstract class PendingTransaction
super(transaction, addedAt);
}
public Priority(final long sequence, final Transaction transaction) {
super(sequence, transaction);
public Priority(final long sequence, final byte score, final Transaction transaction) {
super(sequence, score, transaction);
}
@Override
public PendingTransaction detachedCopy() {
return new Priority(getSequence(), getTransaction().detachedCopy());
return new Priority(getSequence(), getScore(), getTransaction().detachedCopy());
}
@Override

View File

@@ -37,12 +37,14 @@ public class TransactionPoolMetrics {
public static final String ADDED_COUNTER_NAME = "added_total";
public static final String REMOVED_COUNTER_NAME = "removed_total";
public static final String REJECTED_COUNTER_NAME = "rejected_total";
public static final String PENALIZED_COUNTER_NAME = "penalized_total";
public static final String EXPIRED_MESSAGES_COUNTER_NAME = "messages_expired_total";
private static final int SKIPPED_MESSAGES_LOGGING_THRESHOLD = 1000;
private final MetricsSystem metricsSystem;
private final LabelledMetric<Counter> addedCounter;
private final LabelledMetric<Counter> removedCounter;
private final LabelledMetric<Counter> rejectedCounter;
private final LabelledMetric<Counter> penalizedCounter;
private final LabelledGauge spaceUsed;
private final LabelledGauge transactionCount;
private final LabelledGauge transactionCountByType;
@@ -88,6 +90,15 @@ public class TransactionPoolMetrics {
"reason",
"layer");
penalizedCounter =
metricsSystem.createLabelledCounter(
BesuMetricCategory.TRANSACTION_POOL,
PENALIZED_COUNTER_NAME,
"Count of penalized transactions in the transaction pool",
"source",
"priority",
"layer");
spaceUsed =
metricsSystem.createLabelledGauge(
BesuMetricCategory.TRANSACTION_POOL,
@@ -246,6 +257,15 @@ public class TransactionPoolMetrics {
.inc();
}
public void incrementPenalized(final PendingTransaction pendingTransaction, final String layer) {
penalizedCounter
.labels(
location(pendingTransaction.isReceivedFromLocalSource()),
priority(pendingTransaction.hasPriority()),
layer)
.inc();
}
public void incrementExpiredMessages(final String message) {
expiredMessagesCounter.labels(message).inc();
}

View File

@@ -24,13 +24,17 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Holds the current set of executable pending transactions, that are candidate for inclusion on
@@ -137,6 +141,13 @@ public abstract class AbstractPrioritizedTransactions extends AbstractSequential
orderByFee.remove(removedTx);
}
@Override
protected void internalPenalize(final PendingTransaction penalizedTx) {
orderByFee.remove(penalizedTx);
penalizedTx.decrementScore();
orderByFee.add(penalizedTx);
}
@Override
public List<PendingTransaction> promote(
final Predicate<PendingTransaction> promotionFilter,
@@ -188,6 +199,60 @@ public abstract class AbstractPrioritizedTransactions extends AbstractSequential
.toList();
}
/**
* Returns pending txs by sender and ordered by score desc. In case a sender has pending txs with
* different scores, then in nonce sequence, every time there is a score decrease, his pending txs
* will be put in a new entry with that score. For example if a sender has 3 pending txs (where
* the first number is the nonce and the score is between parenthesis): 0(127), 1(126), 2(127),
* then for he there will be 2 entries:
*
* <ul>
* <li>0(127)
* <li>1(126), 2(127)
* </ul>
*
* @return pending txs by sender and ordered by score desc
*/
public NavigableMap<Byte, List<SenderPendingTransactions>> getByScore() {
final var sendersToAdd = new HashSet<>(txsBySender.keySet());
return orderByFee.descendingSet().stream()
.map(PendingTransaction::getSender)
.filter(sendersToAdd::remove)
.flatMap(sender -> splitByScore(sender, txsBySender.get(sender)).entrySet().stream())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> {
a.addAll(b);
return a;
},
TreeMap::new))
.descendingMap();
}
private Map<Byte, List<SenderPendingTransactions>> splitByScore(
final Address sender, final NavigableMap<Long, PendingTransaction> txsBySender) {
final var splitByScore = new HashMap<Byte, List<SenderPendingTransactions>>();
byte currScore = txsBySender.firstEntry().getValue().getScore();
var currSplit = new ArrayList<PendingTransaction>();
for (final var entry : txsBySender.entrySet()) {
if (entry.getValue().getScore() < currScore) {
// score decreased, we need to save current split and start a new one
splitByScore
.computeIfAbsent(currScore, k -> new ArrayList<>())
.add(new SenderPendingTransactions(sender, currSplit));
currSplit = new ArrayList<>();
currScore = entry.getValue().getScore();
}
currSplit.add(entry.getValue());
}
splitByScore
.computeIfAbsent(currScore, k -> new ArrayList<>())
.add(new SenderPendingTransactions(sender, currSplit));
return splitByScore;
}
@Override
protected long cacheFreeSpace() {
return Integer.MAX_VALUE;

View File

@@ -463,6 +463,18 @@ public abstract class AbstractTransactionsLayer implements TransactionsLayer {
}
}
@Override
public void penalize(final PendingTransaction penalizedTransaction) {
if (pendingTransactions.containsKey(penalizedTransaction.getHash())) {
internalPenalize(penalizedTransaction);
metrics.incrementPenalized(penalizedTransaction, name());
} else {
nextLayer.penalize(penalizedTransaction);
}
}
protected abstract void internalPenalize(final PendingTransaction pendingTransaction);
/**
* How many txs of a specified type can be promoted? This make sense when a max number of txs of a
* type can be included in a single block (ex. blob txs), to avoid filling the layer with more txs

View File

@@ -19,7 +19,6 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
@@ -66,7 +65,8 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
@Override
protected int compareByFee(final PendingTransaction pt1, final PendingTransaction pt2) {
return Comparator.comparing(PendingTransaction::hasPriority)
return Comparator.comparing(PendingTransaction::getScore)
.thenComparing(PendingTransaction::hasPriority)
.thenComparing(
(PendingTransaction pendingTransaction) ->
pendingTransaction.getTransaction().getEffectivePriorityFeePerGas(nextBlockBaseFee))
@@ -195,8 +195,8 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
return "Basefee Prioritized: Empty";
}
final Transaction highest = orderByFee.last().getTransaction();
final Transaction lowest = orderByFee.first().getTransaction();
final PendingTransaction highest = orderByFee.last();
final PendingTransaction lowest = orderByFee.first();
return "Basefee Prioritized: "
+ "count: "
@@ -205,16 +205,26 @@ public class BaseFeePrioritizedTransactions extends AbstractPrioritizedTransacti
+ spaceUsed
+ ", unique senders: "
+ txsBySender.size()
+ ", highest priority tx: [max fee: "
+ highest.getMaxGasPrice().toHumanReadableString()
+ ", highest priority tx: [score: "
+ highest.getScore()
+ ", max fee: "
+ highest.getTransaction().getMaxGasPrice().toHumanReadableString()
+ ", curr prio fee: "
+ highest.getEffectivePriorityFeePerGas(nextBlockBaseFee).toHumanReadableString()
+ highest
.getTransaction()
.getEffectivePriorityFeePerGas(nextBlockBaseFee)
.toHumanReadableString()
+ ", hash: "
+ highest.getHash()
+ "], lowest priority tx: [max fee: "
+ lowest.getMaxGasPrice().toHumanReadableString()
+ "], lowest priority tx: [score: "
+ lowest.getScore()
+ ", max fee: "
+ lowest.getTransaction().getMaxGasPrice().toHumanReadableString()
+ ", curr prio fee: "
+ lowest.getEffectivePriorityFeePerGas(nextBlockBaseFee).toHumanReadableString()
+ lowest
.getTransaction()
.getEffectivePriorityFeePerGas(nextBlockBaseFee)
.toHumanReadableString()
+ ", hash: "
+ lowest.getHash()
+ "], next block base fee: "

View File

@@ -85,6 +85,9 @@ public class EndLayer implements TransactionsLayer {
@Override
public void remove(final PendingTransaction pendingTransaction, final RemovalReason reason) {}
@Override
public void penalize(final PendingTransaction penalizedTx) {}
@Override
public void blockAdded(
final FeeMarket feeMarket,

View File

@@ -56,7 +56,8 @@ public class GasPricePrioritizedTransactions extends AbstractPrioritizedTransact
@Override
protected int compareByFee(final PendingTransaction pt1, final PendingTransaction pt2) {
return comparing(PendingTransaction::hasPriority)
return comparing(PendingTransaction::getScore)
.thenComparing(PendingTransaction::hasPriority)
.thenComparing(PendingTransaction::getGasPrice)
.thenComparing(PendingTransaction::getSequence)
.compare(pt1, pt2);
@@ -78,21 +79,33 @@ public class GasPricePrioritizedTransactions extends AbstractPrioritizedTransact
}
@Override
public String internalLogStats() {
protected String internalLogStats() {
if (orderByFee.isEmpty()) {
return "GasPrice Prioritized: Empty";
}
final PendingTransaction highest = orderByFee.last();
final PendingTransaction lowest = orderByFee.first();
return "GasPrice Prioritized: "
+ "count: "
+ pendingTransactions.size()
+ " space used: "
+ ", space used: "
+ spaceUsed
+ " unique senders: "
+ ", unique senders: "
+ txsBySender.size()
+ ", highest fee tx: "
+ orderByFee.last().getTransaction().getGasPrice().get().toHumanReadableString()
+ ", lowest fee tx: "
+ orderByFee.first().getTransaction().getGasPrice().get().toHumanReadableString();
+ ", highest priority tx: [score: "
+ highest.getScore()
+ ", gas price: "
+ highest.getTransaction().getGasPrice().get().toHumanReadableString()
+ ", hash: "
+ highest.getHash()
+ "], lowest priority tx: [score: "
+ lowest.getScore()
+ ", gas price: "
+ lowest.getTransaction().getGasPrice().get().toHumanReadableString()
+ ", hash: "
+ lowest.getHash()
+ "]";
}
}

View File

@@ -42,10 +42,12 @@ import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import java.util.ArrayDeque;
import java.util.ArrayList;
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;
@@ -314,55 +316,80 @@ public class LayeredPendingTransactions implements PendingTransactions {
@Override
public void selectTransactions(final PendingTransactions.TransactionSelector selector) {
final List<PendingTransaction> invalidTransactions = new ArrayList<>();
final List<PendingTransaction> penalizedTransactions = new ArrayList<>();
final Set<Address> skipSenders = new HashSet<>();
final List<SenderPendingTransactions> candidateTxsBySender;
final Map<Byte, List<SenderPendingTransactions>> candidateTxsByScore;
synchronized (this) {
// since selecting transactions for block creation is a potential long operation
// we want to avoid to keep the lock for all the process, but we just lock to get
// the candidate transactions
candidateTxsBySender = prioritizedTransactions.getBySender();
candidateTxsByScore = prioritizedTransactions.getByScore();
}
selection:
for (final var senderTxs : candidateTxsBySender) {
LOG.trace("highPrioSenderTxs {}", senderTxs);
for (final var entry : candidateTxsByScore.entrySet()) {
LOG.trace("Evaluating txs with score {}", entry.getKey());
for (final var candidatePendingTx : senderTxs.pendingTransactions()) {
final var selectionResult = selector.evaluateTransaction(candidatePendingTx);
for (final var senderTxs : entry.getValue()) {
LOG.trace("Evaluating sender txs {}", senderTxs);
LOG.atTrace()
.setMessage("Selection result {} for transaction {}")
.addArgument(selectionResult)
.addArgument(candidatePendingTx::toTraceLog)
.log();
if (!skipSenders.contains(senderTxs.sender())) {
if (selectionResult.discard()) {
invalidTransactions.add(candidatePendingTx);
logDiscardedTransaction(candidatePendingTx, selectionResult);
}
for (final var candidatePendingTx : senderTxs.pendingTransactions()) {
final var selectionResult = selector.evaluateTransaction(candidatePendingTx);
if (selectionResult.stop()) {
LOG.trace("Stopping selection");
break selection;
}
LOG.atTrace()
.setMessage("Selection result {} for transaction {}")
.addArgument(selectionResult)
.addArgument(candidatePendingTx::toTraceLog)
.log();
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());
break;
if (selectionResult.discard()) {
invalidTransactions.add(candidatePendingTx);
logDiscardedTransaction(candidatePendingTx, selectionResult);
}
if (selectionResult.penalize()) {
penalizedTransactions.add(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;
}
}
}
}
}
ethScheduler.scheduleTxWorkerTask(
() ->
invalidTransactions.forEach(
invalidTx -> {
synchronized (this) {
prioritizedTransactions.remove(invalidTx, INVALIDATED);
}
}));
() -> {
invalidTransactions.forEach(
invalidTx -> {
synchronized (this) {
prioritizedTransactions.remove(invalidTx, INVALIDATED);
}
});
penalizedTransactions.forEach(
penalizedTx -> {
synchronized (this) {
prioritizedTransactions.internalPenalize(penalizedTx);
}
});
});
}
@Override

View File

@@ -18,7 +18,6 @@ import static org.hyperledger.besu.ethereum.eth.transactions.layered.Transaction
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.BlobCache;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
@@ -43,7 +42,8 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer {
private final NavigableSet<PendingTransaction> orderByMaxFee =
new TreeSet<>(
Comparator.comparing(PendingTransaction::hasPriority)
Comparator.comparing(PendingTransaction::getScore)
.thenComparing(PendingTransaction::hasPriority)
.thenComparing((PendingTransaction pt) -> pt.getTransaction().getMaxGasPrice())
.thenComparing(PendingTransaction::getSequence));
@@ -116,6 +116,20 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer {
}
}
@Override
protected void internalPenalize(final PendingTransaction penalizedTx) {
final var senderTxs = txsBySender.get(penalizedTx.getSender());
if (senderTxs.firstKey() == penalizedTx.getNonce()) {
// since we only sort the first tx of sender, we only need to re-sort in this case
orderByMaxFee.remove(penalizedTx);
penalizedTx.decrementScore();
orderByMaxFee.add(penalizedTx);
} else {
// otherwise we just decrement the score
penalizedTx.decrementScore();
}
}
@Override
protected void internalReplaced(final PendingTransaction replacedTx) {
orderByMaxFee.remove(replacedTx);
@@ -213,8 +227,8 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer {
return "Ready: Empty";
}
final Transaction top = orderByMaxFee.last().getTransaction();
final Transaction last = orderByMaxFee.first().getTransaction();
final PendingTransaction top = orderByMaxFee.last();
final PendingTransaction last = orderByMaxFee.first();
return "Ready: "
+ "count="
@@ -223,12 +237,16 @@ public class ReadyTransactions extends AbstractSequentialTransactionsLayer {
+ spaceUsed
+ ", unique senders: "
+ txsBySender.size()
+ ", top by max fee[max fee:"
+ top.getMaxGasPrice().toHumanReadableString()
+ ", top by score and max gas price[score: "
+ top.getScore()
+ ", max gas price:"
+ top.getTransaction().getMaxGasPrice().toHumanReadableString()
+ ", hash: "
+ top.getHash()
+ "], last by max fee [max fee: "
+ last.getMaxGasPrice().toHumanReadableString()
+ "], last by score and max gas price [score: "
+ last.getScore()
+ ", max fee: "
+ last.getTransaction().getMaxGasPrice().toHumanReadableString()
+ ", hash: "
+ last.getHash()
+ "]";

View File

@@ -303,6 +303,11 @@ public class SparseTransactions extends AbstractTransactionsLayer {
}
}
@Override
protected void internalPenalize(final PendingTransaction penalizedTx) {
// intentionally no-op
}
private void deleteGap(final Address sender) {
orderByGap.get(gapBySender.remove(sender)).remove(sender);
}

View File

@@ -45,6 +45,19 @@ public interface TransactionsLayer {
void remove(PendingTransaction pendingTransaction, RemovalReason reason);
/**
* Penalize a pending transaction. Penalization could be applied to notify the txpool that this
* pending tx has some temporary issues that prevent it from being included in a block, and so it
* should be de-prioritized in some ways, so it will be re-evaluated only after non penalized
* pending txs. For example: if during the evaluation for block inclusion, the pending tx is
* excluded because the sender has not enough balance to send it, this could be a transient issue
* since later the sender could receive some funds, but in any case we penalize the pending tx, so
* it is pushed down in the order of prioritized pending txs.
*
* @param penalizedTransaction the tx to penalize
*/
void penalize(PendingTransaction penalizedTransaction);
void blockAdded(
FeeMarket feeMarket,
BlockHeader blockHeader,

View File

@@ -22,7 +22,8 @@
* transactions that could be selected for a future block proposal, and at the same time, without
* penalizing legitimate unordered transactions, that are only temporary non-executable.
*
* <p>It is disabled by default, to enable use the option {@code Xlayered-tx-pool=true}
* <p>It is enabled by default on public networks, to switch to another implementation use the
* option {@code tx-pool}
*
* <p>The main idea is to organize the txpool in an arbitrary number of layers, where each layer has
* specific rules and constraints that determine if a transaction belong or not to that layer and
@@ -38,6 +39,14 @@
* transactions are removed since confirmed in a block, transactions from the next layer are
* promoted until there is space.
*
* <p>Some layers could make use of the score of a pending transactions, to push back in the rank
* those pending transactions that have been penalized.
*
* <p>Layers are not thread safe, since they are not meant to be accessed directly, and all the
* synchronization is managed at the level of {@link
* org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredPendingTransactions
* LayeredPendingTransactions} class.
*
* <p>The current implementation is based on 3 layers, plus the last one that just drop every
* transaction when the previous layers are full. The 3 layers are, in order:
*
@@ -48,20 +57,20 @@
* </ul>
*
* <p>Prioritized: This is where candidate transactions are selected for creating a new block.
* Transactions ordered by the effective priority fee, and it is limited by size, 2000 by default,
* to reduce the overhead of the sorting and because that number is enough to fill any block, at the
* current gas limit. Does not allow nonce gaps, and the first transaction for each sender must be
* the next one for that sender. Eviction is done removing the transaction with the higher nonce for
* the sender of the less valuable transaction, to avoid creating nonce gaps, evicted transactions
* go into the next layer Ready.
* Transactions ordered by score and then effective priority fee, and it is limited by size, 2000 by
* default, to reduce the overhead of the sorting and because that number is enough to fill any
* block, at the current gas limit. Does not allow nonce gaps, and the first transaction for each
* sender must be the next one for that sender. Eviction is done removing the transaction with the
* higher nonce for the sender of the less score and less effective priority fee transaction, to
* avoid creating nonce gaps, evicted transactions go into the next layer Ready.
*
* <p>Ready: Similar to the Prioritized, it does not allow nonce gaps, and the first transaction for
* each sender must be the next one for that sender, but it is limited by space instead of count,
* thus allowing many more transactions, think about this layer like a buffer for the Prioritized.
* Since it is meant to keep ten to hundreds of thousand of transactions, it does not have a full
* ordering, like the previous, but only the first transaction for each sender is ordered using a
* stable value that is the max fee per gas. Eviction is the same as the Prioritized, and evicted
* transaction go into the next layer Sparse.
* stable value that is score and then max fee per gas. Eviction is the same as the Prioritized, and
* evicted transaction go into the next layer Sparse.
*
* <p>Sparse: This is the first layer where nonce gaps are allowed and where the first transaction
* for a sender could not be the next expected one for that sender. The main purpose of this layer

View File

@@ -159,6 +159,12 @@ public class LayersTest extends BaseTransactionPoolTest {
assertScenario(scenario, BLOB_TX_POOL_CONFIG);
}
@ParameterizedTest
@MethodSource("providerPenalized")
void penalized(final Scenario scenario) {
assertScenario(scenario);
}
private void assertScenario(final Scenario scenario) {
assertScenario(scenario, DEFAULT_TX_POOL_CONFIG);
}
@@ -1256,6 +1262,79 @@ public class LayersTest extends BaseTransactionPoolTest {
.expectedSparseForSenders()));
}
static Stream<Arguments> providerPenalized() {
return Stream.of(
Arguments.of(
new Scenario("single sender, single tx")
.addForSender(S1, 0)
.expectedPrioritizedForSender(S1, 0)
.penalizeForSender(S1, 0)
.expectedPrioritizedForSender(S1, 0)),
Arguments.of(
new Scenario("single sender penalize last")
.addForSender(S1, 0, 1)
.expectedPrioritizedForSender(S1, 0, 1)
.penalizeForSender(S1, 1)
.expectedPrioritizedForSender(S1, 0, 1)),
Arguments.of(
new Scenario("single sender penalize first")
.addForSender(S1, 0, 1)
.expectedPrioritizedForSender(S1, 0, 1)
.penalizeForSender(S1, 0, 1)
// even if 0 has less score it is always the first for the sender
// since otherwise there is a nonce gap
.expectedPrioritizedForSender(S1, 0, 1)),
Arguments.of(
new Scenario("multiple senders, penalize top")
.addForSenders(S1, 0, S2, 0)
// remember S2 pays more fees
.expectedPrioritizedForSenders(S2, 0, S1, 0)
.penalizeForSender(S2, 0)
.expectedPrioritizedForSenders(S1, 0, S2, 0)),
Arguments.of(
new Scenario("multiple senders, penalize bottom")
.addForSenders(S1, 0, S2, 0)
.expectedPrioritizedForSenders(S2, 0, S1, 0)
.penalizeForSender(S1, 0)
.expectedPrioritizedForSenders(S2, 0, S1, 0)),
Arguments.of(
new Scenario("multiple senders, penalize middle")
.addForSenders(S1, 0, S2, 0, S3, 0)
.expectedPrioritizedForSenders(S3, 0, S2, 0, S1, 0)
.penalizeForSender(S2, 0)
.expectedPrioritizedForSenders(S3, 0, S1, 0, S2, 0)),
Arguments.of(
new Scenario("single sender, promote from ready")
.addForSender(S1, 0, 1, 2, 3, 4, 5)
.expectedPrioritizedForSender(S1, 0, 1, 2)
.expectedReadyForSender(S1, 3, 4, 5)
.penalizeForSender(S1, 3)
.confirmedForSenders(S1, 0)
// even if penalized 3 is promoted to avoid nonce gap
.expectedPrioritizedForSender(S1, 1, 2, 3)
.expectedReadyForSender(S1, 4, 5)),
Arguments.of(
new Scenario("multiple senders, overflow to ready")
.addForSenders(S1, 0, S2, 0, S3, 0)
.expectedPrioritizedForSenders(S3, 0, S2, 0, S1, 0)
.expectedReadyForSenders()
.penalizeForSender(S3, 0)
.addForSender(S1, 1)
.expectedPrioritizedForSenders(S2, 0, S1, 0, S1, 1)
// S3(0) is demoted to ready even if it is paying more fees,
// since has a lower score
.expectedReadyForSender(S3, 0)),
Arguments.of(
new Scenario("multiple senders, overflow to sparse")
.addForSenders(S1, 0, S2, 0, S3, 0, S1, 1, S2, 1, S3, 1)
.expectedPrioritizedForSenders(S3, 0, S3, 1, S2, 0)
.expectedReadyForSenders(S2, 1, S1, 0, S1, 1)
.penalizeForSender(S2, 1)
.addForSender(S2, 2)
.expectedReadyForSenders(S1, 0, S1, 1, S2, 1)
.expectedSparseForSender(S2, 2)));
}
private static BlockHeader mockBlockHeader() {
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getBaseFee()).thenReturn(Optional.of(BASE_FEE));
@@ -1511,10 +1590,12 @@ public class LayersTest extends BaseTransactionPoolTest {
private void assertExpectedPrioritized(
final AbstractPrioritizedTransactions prioLayer, final List<PendingTransaction> expected) {
assertThat(prioLayer.getBySender())
.describedAs("Prioritized")
.flatExtracting(SenderPendingTransactions::pendingTransactions)
.containsExactlyElementsOf(expected);
final var flatOrder =
prioLayer.getByScore().values().stream()
.flatMap(List::stream)
.flatMap(spt -> spt.pendingTransactions().stream())
.toList();
assertThat(flatOrder).describedAs("Prioritized").containsExactlyElementsOf(expected);
}
private void assertExpectedReady(
@@ -1582,6 +1663,23 @@ public class LayersTest extends BaseTransactionPoolTest {
return this;
}
public Scenario penalizeForSender(final Sender sender, final long... nonce) {
Arrays.stream(nonce)
.forEach(
n -> {
actions.add(
(pending, prio, ready, sparse, dropped) -> {
final var senderTxs = prio.getAllFor(sender.address);
Arrays.stream(nonce)
.mapToObj(
n2 -> senderTxs.stream().filter(pt -> pt.getNonce() == n2).findAny())
.map(Optional::get)
.forEach(prio::penalize);
});
});
return this;
}
public Scenario expectedSelectedTransactions(final Object... args) {
List<PendingTransaction> expectedSelected = new ArrayList<>();
for (int i = 0; i < args.length; i = i + 2) {

View File

@@ -232,7 +232,7 @@ public class StateTestSubCommand implements Runnable {
}
final BlockHeader blockHeader = spec.getBlockHeader();
final Transaction transaction = spec.getTransaction();
final Transaction transaction = spec.getTransaction(0);
final ObjectNode summaryLine = objectMapper.createObjectNode();
if (transaction == null) {
if (parentCommand.showJsonAlloc || parentCommand.showJsonResults) {

View File

@@ -204,7 +204,7 @@ dependencies {
referenceTestImplementation project(path: ':testutil')
referenceTestImplementation project(path: ':util')
// the following will be resolved via custom ivy repository declared in root build.gradle
referenceTestImplementation 'ethereum:execution-spec-tests:2.1.1:fixtures@tar.gz'
referenceTestImplementation 'ethereum:execution-spec-tests:3.0.0:fixtures_stable@tar.gz'
referenceTestImplementation 'com.fasterxml.jackson.core:jackson-databind'
referenceTestImplementation 'com.google.guava:guava'
referenceTestImplementation 'io.tmio:tuweni-bytes'

View File

@@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import java.util.List;
import java.util.function.Supplier;
public class GeneralStateTestCaseEipSpec {
@@ -33,7 +34,7 @@ public class GeneralStateTestCaseEipSpec {
// anything
// is run, which isn't friendly and 2) this makes it harder to parallelize this step. Anyway, this
// is why this is a supplier: calling get() actually does the signing.
private final Supplier<Transaction> transactionSupplier;
private final List<Supplier<Transaction>> transactionSuppliers;
private final ReferenceTestWorldState initialWorldState;
@@ -51,7 +52,7 @@ public class GeneralStateTestCaseEipSpec {
GeneralStateTestCaseEipSpec(
final String fork,
final Supplier<Transaction> transactionSupplier,
final List<Supplier<Transaction>> transactionSuppliers,
final ReferenceTestWorldState initialWorldState,
final Hash expectedRootHash,
final Hash expectedLogsHash,
@@ -61,7 +62,7 @@ public class GeneralStateTestCaseEipSpec {
final int valueIndex,
final String expectException) {
this.fork = fork;
this.transactionSupplier = transactionSupplier;
this.transactionSuppliers = transactionSuppliers;
this.initialWorldState = initialWorldState;
this.expectedRootHash = expectedRootHash;
this.expectedLogsHash = expectedLogsHash;
@@ -88,9 +89,13 @@ public class GeneralStateTestCaseEipSpec {
return expectedLogsHash;
}
public Transaction getTransaction() {
public int getTransactionsCount() {
return transactionSuppliers.size();
}
public Transaction getTransaction(final int txIndex) {
try {
return transactionSupplier.get();
return transactionSuppliers.get(txIndex).get();
} catch (RuntimeException re) {
// some tests specify invalid transactions. We throw exceptions in
// GeneralStateTests but they are encoded in BlockchainTests, so we

View File

@@ -72,11 +72,12 @@ public class GeneralStateTestCaseSpec {
.stateRoot(p.rootHash)
.blockHeaderFunctions(MAINNET_FUNCTIONS)
.buildBlockHeader();
final Supplier<Transaction> txSupplier = () -> versionedTransaction.get(p.indexes);
final List<Supplier<Transaction>> txSupplierList =
List.of(() -> versionedTransaction.get(p.indexes));
specs.add(
new GeneralStateTestCaseEipSpec(
eip,
txSupplier,
txSupplierList,
initialWorldState,
p.rootHash,
p.logsHash,

View File

@@ -120,7 +120,7 @@ public class GeneralStateReferenceTestTools {
public static void executeTest(final GeneralStateTestCaseEipSpec spec) {
final BlockHeader blockHeader = spec.getBlockHeader();
final ReferenceTestWorldState initialWorldState = spec.getInitialWorldState();
final Transaction transaction = spec.getTransaction();
final Transaction transaction = spec.getTransaction(0);
ProtocolSpec protocolSpec = protocolSpec(spec.getFork());
BlockchainReferenceTestTools.verifyJournaledEVMAccountCompatability(initialWorldState, protocolSpec);

View File

@@ -243,8 +243,8 @@ public class MainnetEVMs {
registry.put(new CodeSizeOperation(gasCalculator));
registry.put(new CodeCopyOperation(gasCalculator));
registry.put(new GasPriceOperation(gasCalculator));
registry.put(new ExtCodeCopyOperation(gasCalculator));
registry.put(new ExtCodeSizeOperation(gasCalculator));
registry.put(new ExtCodeCopyOperation(gasCalculator, false));
registry.put(new ExtCodeSizeOperation(gasCalculator, false));
registry.put(new BlockHashOperation(gasCalculator));
registry.put(new CoinbaseOperation(gasCalculator));
registry.put(new TimestampOperation(gasCalculator));
@@ -481,7 +481,7 @@ public class MainnetEVMs {
registry.put(new SarOperation(gasCalculator));
registry.put(new ShlOperation(gasCalculator));
registry.put(new ShrOperation(gasCalculator));
registry.put(new ExtCodeHashOperation(gasCalculator));
registry.put(new ExtCodeHashOperation(gasCalculator, false));
}
/**
@@ -1178,6 +1178,11 @@ public class MainnetEVMs {
registry.put(new SwapNOperation(gasCalculator));
registry.put(new ExchangeOperation(gasCalculator));
// EIP-3540 EOF Aware EXTCODE* operations
registry.put(new ExtCodeCopyOperation(gasCalculator, true));
registry.put(new ExtCodeHashOperation(gasCalculator, true));
registry.put(new ExtCodeSizeOperation(gasCalculator, true));
// EIP-4200 relative jump
registry.put(new RelativeJumpOperation(gasCalculator));
registry.put(new RelativeJumpIfOperation(gasCalculator));

View File

@@ -34,13 +34,26 @@ 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");
private final boolean enableEIP3540;
/**
* Instantiates a new Ext code copy operation.
*
* @param gasCalculator the gas calculator
*/
public ExtCodeCopyOperation(final GasCalculator gasCalculator) {
this(gasCalculator, false);
}
/**
* Instantiates a new Ext code copy operation.
*
* @param gasCalculator the gas calculator
* @param enableEIP3540 enable EIP-3540 semantics (don't copy EOF)
*/
public ExtCodeCopyOperation(final GasCalculator gasCalculator, final boolean enableEIP3540) {
super(0x3C, "EXTCODECOPY", 4, 0, gasCalculator);
this.enableEIP3540 = enableEIP3540;
}
/**
@@ -82,7 +95,10 @@ public class ExtCodeCopyOperation extends AbstractOperation {
final Account account = frame.getWorldUpdater().get(address);
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) {
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
frame.writeMemory(memOffset, sourceOffset, numBytes, EOF_REPLACEMENT_CODE);
} else {
frame.writeMemory(memOffset, sourceOffset, numBytes, code);

View File

@@ -34,13 +34,26 @@ public class ExtCodeHashOperation extends AbstractOperation {
// // 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5
static final Hash EOF_REPLACEMENT_HASH = Hash.hash(ExtCodeCopyOperation.EOF_REPLACEMENT_CODE);
private final boolean enableEIP3540;
/**
* Instantiates a new Ext code hash operation.
*
* @param gasCalculator the gas calculator
*/
public ExtCodeHashOperation(final GasCalculator gasCalculator) {
this(gasCalculator, false);
}
/**
* Instantiates a new Ext code copy operation.
*
* @param gasCalculator the gas calculator
* @param enableEIP3540 enable EIP-3540 semantics (don't copy EOF)
*/
public ExtCodeHashOperation(final GasCalculator gasCalculator, final boolean enableEIP3540) {
super(0x3F, "EXTCODEHASH", 1, 1, gasCalculator);
this.enableEIP3540 = enableEIP3540;
}
/**
@@ -71,7 +84,10 @@ public class ExtCodeHashOperation extends AbstractOperation {
frame.pushStackItem(Bytes.EMPTY);
} else {
final Bytes code = account.getCode();
if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) {
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
frame.pushStackItem(EOF_REPLACEMENT_HASH);
} else {
frame.pushStackItem(account.getCodeHash());

View File

@@ -32,13 +32,26 @@ public class ExtCodeSizeOperation extends AbstractOperation {
static final Bytes EOF_SIZE = Bytes.of(2);
private final boolean enableEIP3540;
/**
* Instantiates a new Ext code size operation.
*
* @param gasCalculator the gas calculator
*/
public ExtCodeSizeOperation(final GasCalculator gasCalculator) {
this(gasCalculator, false);
}
/**
* Instantiates a new Ext code size operation.
*
* @param gasCalculator the gas calculator
* @param enableEIP3540 enable EIP-3540 semantics (EOF is size 2)
*/
public ExtCodeSizeOperation(final GasCalculator gasCalculator, final boolean enableEIP3540) {
super(0x3B, "EXTCODESIZE", 1, 1, gasCalculator);
this.enableEIP3540 = enableEIP3540;
}
/**
@@ -70,7 +83,10 @@ public class ExtCodeSizeOperation extends AbstractOperation {
codeSize = Bytes.EMPTY;
} else {
final Bytes code = account.getCode();
if (code.size() >= 2 && code.get(0) == EOFLayout.EOF_PREFIX_BYTE && code.get(1) == 0) {
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
codeSize = EOF_SIZE;
} else {
codeSize = Words.intBytes(code.size());

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -26,8 +26,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.BaseFeeOperation;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import java.util.Optional;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -25,8 +25,6 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.operation.BlobHashOperation;
import org.hyperledger.besu.evm.operation.Operation;
import java.util.ArrayList;
import java.util.Arrays;

View File

@@ -12,14 +12,13 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.operation.BlockHashOperation;
import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode;
@@ -24,8 +24,6 @@ import org.hyperledger.besu.evm.code.CodeSection;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.ReturnStack;
import org.hyperledger.besu.evm.operation.CallFOperation;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.apache.tuweni.bytes.Bytes;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -20,7 +20,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.operation.ChainIdOperation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import java.util.List;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID;
@@ -35,7 +35,6 @@ import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.Create2Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.MainnetEVMs.DEV_NET_CHAIN_ID;
@@ -34,7 +34,6 @@ import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.CreateOperation;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.tracing.OperationTracer;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
@@ -24,8 +24,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.DataCopyOperation;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import java.util.Arrays;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -29,8 +29,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.AbstractExtCallOperation;
import org.hyperledger.besu.evm.operation.ExtCallOperation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

View File

@@ -0,0 +1,245 @@
/*
* 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.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.toy.ToyWorld;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Arrays;
import java.util.Collection;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class ExtCodeCopyOperationTest {
private static final Address REQUESTED_ADDRESS = Address.fromHexString("0x22222222");
private final ToyWorld toyWorld = new ToyWorld();
private final WorldUpdater worldStateUpdater = toyWorld.updater();
@Mock EVM evm;
static Collection<Object[]> extCodeCopyTestVector() {
return Arrays.asList(
new Object[][] {
{
"Copy after, no overlap",
Bytes.fromHexString("0123456789abcdef000000000000000000000000000000000000000000000000"),
32,
0,
8,
Bytes.fromHexString(
"00000000000000000000000000000000000000000000000000000000000000000123456789abcdef"),
false,
2609L
},
{
"copy from uninitialized memory",
Bytes.EMPTY,
0,
24,
16,
Bytes.fromHexString(
"0x000000000000000000000000000000000000000000000000000000000000000000"),
false,
2606L
},
{
"copy from initialized + uninitialized memory",
Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000123456789abcdef"),
64,
24,
16,
Bytes.fromHexString(
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123456789abcdef000000000000000000000000000000000000000000000000"),
false,
2612L
},
{
"overlapping src < dst",
Bytes.fromHexString(
"0x0123456789abcdef000000000000000000000000000000000000000000000000"),
4,
0,
8,
Bytes.fromHexString(
"0x000000000123456789abcdef0000000000000000000000000000000000000000"),
false,
2606L
},
{
"overlapping src > dst",
Bytes.fromHexString(
"0x00112233445566778899aabbccddeeff00000000000000000000000000000000"),
0,
4,
8,
Bytes.fromHexString(
"0x445566778899aabb000000000000000000000000000000000000000000000000"),
false,
2606L
},
{
"overlapping src == dst",
Bytes.fromHexString(
"0x00112233445566778899aabbccddeeff00000000000000000000000000000000"),
4,
4,
8,
Bytes.fromHexString(
"0x00000000445566778899aabb0000000000000000000000000000000000000000"),
false,
2606L
},
{
"EOF-reserved pre-eof",
Bytes.fromHexString("0xEF009f918bf09f9fa9"),
0,
0,
9,
Bytes.fromHexString("0xEF009f918bf09f9fa9"),
false,
2606L
},
{
"EOF-reserved post-epf",
Bytes.fromHexString("0xEF009f918bf09f9fa9"),
0,
0,
9,
Bytes.fromHexString("0xEF000000000000000000"),
true,
2606L
},
{
"EF-reserved pre-epf",
Bytes.fromHexString("0xEFF09f918bf09f9fa9"),
0,
0,
9,
Bytes.fromHexString("0xEFF09f918bf09f9fa9"),
false,
2606L
},
{
"EOF-reserved post-eof",
Bytes.fromHexString("0xEFF09f918bf09f9fa9"),
0,
0,
9,
Bytes.fromHexString("0xEFF09f918bf09f9fa9"),
true,
2606L
}
});
}
@SuppressWarnings("unused")
@ParameterizedTest(name = "{0}")
@MethodSource("extCodeCopyTestVector")
void testExtCodeCopy(
final String name,
final Bytes code,
final long dst,
final long src,
final long len,
final Bytes expected,
final boolean eof,
final long gasCost) {
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
ExtCodeCopyOperation subject = new ExtCodeCopyOperation(new PragueGasCalculator(), eof);
MessageFrame frame =
new TestMessageFrameBuilder()
.worldUpdater(worldStateUpdater)
.pushStackItem(Bytes.ofUnsignedLong(len))
.pushStackItem(Bytes.ofUnsignedLong(src))
.pushStackItem(Bytes.ofUnsignedLong(dst))
.pushStackItem(REQUESTED_ADDRESS)
.build();
Operation.OperationResult result = subject.execute(frame, evm);
assertThat(frame.readMemory(0, expected.size())).isEqualTo(expected);
assertThat(frame.memoryWordSize()).isEqualTo((expected.size() + 31) / 32);
assertThat(result.getGasCost()).isEqualTo(gasCost);
}
@Test
void testExtCodeCopyCold() {
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
Bytes code = Bytes.fromHexString("0xEFF09f918bf09f9fa9");
account.setCode(code);
ExtCodeCopyOperation subject = new ExtCodeCopyOperation(new PragueGasCalculator(), false);
MessageFrame frame =
new TestMessageFrameBuilder()
.worldUpdater(worldStateUpdater)
.pushStackItem(Bytes.ofUnsignedLong(9))
.pushStackItem(Bytes.ofUnsignedLong(0))
.pushStackItem(Bytes.ofUnsignedLong(0))
.pushStackItem(REQUESTED_ADDRESS)
.build();
frame.warmUpAddress(REQUESTED_ADDRESS);
Operation.OperationResult result = subject.execute(frame, evm);
assertThat(frame.readMemory(0, 9)).isEqualTo(code);
assertThat(frame.memoryWordSize()).isEqualTo(1);
assertThat(result.getGasCost()).isEqualTo(106);
}
@Test
void testExtCodeEOFDirtyMemory() {
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
Bytes code = Bytes.fromHexString("0xEF009f918bf09f9fa9");
account.setCode(code);
ExtCodeCopyOperation subject = new ExtCodeCopyOperation(new PragueGasCalculator(), true);
MessageFrame frame =
new TestMessageFrameBuilder()
.worldUpdater(worldStateUpdater)
.pushStackItem(Bytes.ofUnsignedLong(9))
.pushStackItem(Bytes.ofUnsignedLong(0))
.pushStackItem(Bytes.ofUnsignedLong(0))
.pushStackItem(REQUESTED_ADDRESS)
.build();
frame.writeMemory(0, 15, Bytes.fromHexString("0x112233445566778899aabbccddeeff"));
Operation.OperationResult result = subject.execute(frame, evm);
assertThat(frame.readMemory(0, 16))
.isEqualTo(Bytes.fromHexString("0xEF0000000000000000aabbccddeeff00"));
assertThat(frame.memoryWordSize()).isEqualTo(1);
assertThat(result.getGasCost()).isEqualTo(2603);
}
}

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
@@ -24,8 +24,8 @@ import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.ExtCodeHashOperation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
@@ -44,9 +44,11 @@ class ExtCodeHashOperationTest {
private final WorldUpdater worldStateUpdater = toyWorld.updater();
private final ExtCodeHashOperation operation =
new ExtCodeHashOperation(new ConstantinopleGasCalculator());
new ExtCodeHashOperation(new ConstantinopleGasCalculator(), false);
private final ExtCodeHashOperation operationIstanbul =
new ExtCodeHashOperation(new IstanbulGasCalculator());
new ExtCodeHashOperation(new IstanbulGasCalculator(), false);
private final ExtCodeHashOperation operationEOF =
new ExtCodeHashOperation(new PragueGasCalculator(), true);
@Test
void shouldCharge400Gas() {
@@ -113,6 +115,50 @@ class ExtCodeHashOperationTest {
assertThat(frame.getStackItem(0)).isEqualTo(Hash.hash(code));
}
@Test
void shouldGetNonEOFHash() {
final Bytes code = Bytes.fromHexString("0xEFF09f918bf09f9fa9");
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
final UInt256 value =
UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS))
.add(UInt256.valueOf(2).pow(UInt256.valueOf(160)));
final MessageFrame frame = createMessageFrame(value);
operation.execute(frame, null);
assertThat(frame.getStackItem(0)).isEqualTo(Hash.hash(code));
final MessageFrame frameIstanbul = createMessageFrame(value);
operationIstanbul.execute(frameIstanbul, null);
assertThat(frameIstanbul.getStackItem(0)).isEqualTo(Hash.hash(code));
final MessageFrame frameEOF = createMessageFrame(value);
operationEOF.execute(frameEOF, null);
assertThat(frameEOF.getStackItem(0)).isEqualTo(Hash.hash(code));
}
@Test
void shouldGetEOFHash() {
final Bytes code = Bytes.fromHexString("0xEF009f918bf09f9fa9");
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
final UInt256 value =
UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS))
.add(UInt256.valueOf(2).pow(UInt256.valueOf(160)));
final MessageFrame frame = createMessageFrame(value);
operation.execute(frame, null);
assertThat(frame.getStackItem(0)).isEqualTo(Hash.hash(code));
final MessageFrame frameIstanbul = createMessageFrame(value);
operationIstanbul.execute(frameIstanbul, null);
assertThat(frameIstanbul.getStackItem(0)).isEqualTo(Hash.hash(code));
final MessageFrame frameEOF = createMessageFrame(value);
operationEOF.execute(frameEOF, null);
assertThat(frameEOF.getStackItem(0)).isEqualTo(Hash.hash(Bytes.fromHexString("0xef00")));
}
private Bytes executeOperation(final Address requestedAddress) {
final MessageFrame frame = createMessageFrame(requestedAddress);
operation.execute(frame, null);

View File

@@ -0,0 +1,183 @@
/*
* Copyright ConsenSys AG.
*
* 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 static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.toy.ToyWorld;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.Test;
class ExtCodeSizeOperationTest {
private static final Address REQUESTED_ADDRESS = Address.fromHexString("0x22222222");
private final ToyWorld toyWorld = new ToyWorld();
private final WorldUpdater worldStateUpdater = toyWorld.updater();
private final ExtCodeSizeOperation operation =
new ExtCodeSizeOperation(new ConstantinopleGasCalculator(), false);
private final ExtCodeSizeOperation operationIstanbul =
new ExtCodeSizeOperation(new IstanbulGasCalculator(), false);
private final ExtCodeSizeOperation operationEOF =
new ExtCodeSizeOperation(new PragueGasCalculator(), true);
@Test
void shouldCharge700Gas() {
final OperationResult result = operation.execute(createMessageFrame(REQUESTED_ADDRESS), null);
assertThat(result.getGasCost()).isEqualTo(700L);
}
@Test
void istanbulShouldCharge700Gas() {
final OperationResult result =
operationIstanbul.execute(createMessageFrame(REQUESTED_ADDRESS), null);
assertThat(result.getGasCost()).isEqualTo(700L);
}
@Test
void shouldReturnZeroWhenAccountDoesNotExist() {
final Bytes result = executeOperation(REQUESTED_ADDRESS);
assertThat(result.trimLeadingZeros()).isEqualTo(Bytes.EMPTY);
}
@Test
void shouldReturnSizeOfEmptyDataWhenAccountExistsButDoesNotHaveCode() {
worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).setBalance(Wei.of(1));
assertThat(executeOperation(REQUESTED_ADDRESS).toInt()).isZero();
}
@Test
void shouldReturnZeroWhenAccountExistsButIsEmpty() {
worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
assertThat(executeOperation(REQUESTED_ADDRESS).trimLeadingZeros()).isEqualTo(Bytes.EMPTY);
}
@Test
void shouldReturnZeroWhenPrecompiledContractHasNoBalance() {
assertThat(executeOperation(Address.ECREC).trimLeadingZeros()).isEqualTo(Bytes.EMPTY);
}
@Test
void shouldReturnEmptyCodeSizeWhenPrecompileHasBalance() {
// Sending money to a precompile causes it to exist in the world state archive.
worldStateUpdater.getOrCreate(Address.ECREC).setBalance(Wei.of(10));
assertThat(executeOperation(Address.ECREC).toInt()).isZero();
}
@Test
void shouldGetSizeOfAccountCodeWhenCodeIsPresent() {
final Bytes code = Bytes.fromHexString("0xabcdef");
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
assertThat(executeOperation(REQUESTED_ADDRESS).toInt()).isEqualTo(3);
}
@Test
void shouldZeroOutLeftMostBitsToGetAddress() {
// If EXTCODESIZE of A is X, then EXTCODESIZE of A + 2**160 is X.
final Bytes code = Bytes.fromHexString("0xabcdef");
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
final UInt256 value =
UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS))
.add(UInt256.valueOf(2).pow(UInt256.valueOf(160)));
final MessageFrame frame = createMessageFrame(value);
operation.execute(frame, null);
assertThat(frame.getStackItem(0).toInt()).isEqualTo(3);
}
@Test
void shouldGetNonEOFSize() {
final Bytes code = Bytes.fromHexString("0xEFF09f918bf09f9fa9");
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
final UInt256 value =
UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS))
.add(UInt256.valueOf(2).pow(UInt256.valueOf(160)));
final MessageFrame frame = createMessageFrame(value);
operation.execute(frame, null);
assertThat(frame.getStackItem(0).toInt()).isEqualTo(9);
final MessageFrame frameIstanbul = createMessageFrame(value);
operationIstanbul.execute(frameIstanbul, null);
assertThat(frame.getStackItem(0).toInt()).isEqualTo(9);
final MessageFrame frameEOF = createMessageFrame(value);
operationEOF.execute(frameEOF, null);
assertThat(frame.getStackItem(0).toInt()).isEqualTo(9);
}
@Test
void shouldGetEOFSize() {
final Bytes code = Bytes.fromHexString("0xEF009f918bf09f9fa9");
final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS);
account.setCode(code);
final UInt256 value =
UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS))
.add(UInt256.valueOf(2).pow(UInt256.valueOf(160)));
final MessageFrame frame = createMessageFrame(value);
operation.execute(frame, null);
assertThat(frame.getStackItem(0).toInt()).isEqualTo(9);
final MessageFrame frameIstanbul = createMessageFrame(value);
operationIstanbul.execute(frameIstanbul, null);
assertThat(frameIstanbul.getStackItem(0).toInt()).isEqualTo(9);
final MessageFrame frameEOF = createMessageFrame(value);
operationEOF.execute(frameEOF, null);
assertThat(frameEOF.getStackItem(0).toInt()).isEqualTo(2);
}
private Bytes executeOperation(final Address requestedAddress) {
final MessageFrame frame = createMessageFrame(requestedAddress);
operation.execute(frame, null);
return frame.getStackItem(0);
}
private MessageFrame createMessageFrame(final Address requestedAddress) {
final UInt256 stackItem = Words.fromAddress(requestedAddress);
return createMessageFrame(stackItem);
}
private MessageFrame createMessageFrame(final UInt256 stackItem) {
final BlockValues blockValues = new FakeBlockValues(1337);
final MessageFrame frame =
new TestMessageFrameBuilder()
.worldUpdater(worldStateUpdater)
.blockValues(blockValues)
.build();
frame.pushStackItem(stackItem);
return frame;
}
}

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -29,8 +29,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.AbstractExtCallOperation;
import org.hyperledger.besu.evm.operation.ExtDelegateCallOperation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -29,8 +29,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueEOFGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.AbstractExtCallOperation;
import org.hyperledger.besu.evm.operation.ExtStaticCallOperation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode;
@@ -23,8 +23,6 @@ import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.code.CodeSection;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.JumpFOperation;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.apache.tuweni.bytes.Bytes;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
@@ -24,7 +24,6 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.JumpOperation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -12,15 +12,13 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.operation.MCopyOperation;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import java.util.Arrays;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -23,8 +23,6 @@ import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.PrevRanDaoOperation;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -24,9 +24,7 @@ import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.Push0Operation;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import java.util.Optional;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode;
@@ -23,10 +23,6 @@ import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.RelativeJumpIfOperation;
import org.hyperledger.besu.evm.operation.RelativeJumpOperation;
import org.hyperledger.besu.evm.operation.RelativeJumpVectorOperation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.apache.tuweni.bytes.Bytes;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.testutils.OperationsTestUtils.mockCode;
@@ -24,8 +24,6 @@ import org.hyperledger.besu.evm.code.CodeSection;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.ReturnStack;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.RetFOperation;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.apache.tuweni.bytes.Bytes;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -20,7 +20,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.operation.RevertOperation;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INSUFFICIENT_GAS;
@@ -25,7 +25,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.SStoreOperation;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder;
import org.hyperledger.besu.evm.toy.ToyWorld;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.operation.SarOperation;
import java.util.List;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.atLeast;
@@ -31,8 +31,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.SelfDestructOperation;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.apache.tuweni.bytes.Bytes;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.operation.ShlOperation;
import java.util.Arrays;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.operation.ShrOperation;
import java.util.Arrays;

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.operations;
package org.hyperledger.besu.evm.operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.INSUFFICIENT_GAS;
@@ -26,8 +26,6 @@ import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.Operation.OperationResult;
import org.hyperledger.besu.evm.operation.TLoadOperation;
import org.hyperledger.besu.evm.operation.TStoreOperation;
import org.hyperledger.besu.evm.testutils.ByteCodeBuilder;
import org.hyperledger.besu.evm.testutils.FakeBlockValues;
import org.hyperledger.besu.evm.testutils.TestCodeExecutor;

View File

@@ -1385,9 +1385,9 @@
<sha256 value="8cf15cd4b93ba1b69cdd928d2c485ee05ccaa0044fed0932e8d0dedfbfa6f093" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="ethereum" name="execution-spec-tests" version="2.1.1">
<artifact name="execution-spec-tests-2.1.1-fixtures.tar.gz">
<sha256 value="817ca59a93dae2cd6458f015dfbc604d234bcbeca63ffe763bd5b5e27ff3c1a1" origin="Generated by Gradle"/>
<component group="ethereum" name="execution-spec-tests" version="3.0.0">
<artifact name="execution-spec-tests-3.0.0-fixtures_stable.tar.gz">
<sha256 value="110c3f7808bebf33541efedb5e57fc22dd1f33f83220343b1d720032739e785b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="info.picocli" name="picocli" version="3.0.0">

View File

@@ -70,7 +70,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = '31xPsOj3mL2Ym+w2GNBJLTw4iWr7y6bVHboZLnsiTFo='
knownHash = '1hrbHRcNGg1EFfXew2Q7skf57jo8L6m+HoGTqgGXilw='
}
check.dependsOn('checkAPIChanges')

View File

@@ -42,6 +42,13 @@ public class TransactionSelectionResult {
*/
boolean discard();
/**
* Should the score of this transaction be decremented?
*
* @return yes if the score of this transaction needs to be decremented
*/
boolean penalize();
/**
* Name of this status
*
@@ -52,30 +59,34 @@ public class TransactionSelectionResult {
private enum BaseStatus implements Status {
SELECTED,
BLOCK_FULL(true, false),
BLOBS_FULL(false, false),
BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false),
BLOCK_SELECTION_TIMEOUT(true, false),
TX_EVALUATION_TOO_LONG(true, true),
INVALID_TRANSIENT(false, false),
INVALID(false, true);
BLOCK_FULL(true, false, false),
BLOBS_FULL(false, false, false),
BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false, false),
BLOCK_SELECTION_TIMEOUT(true, false, false),
TX_EVALUATION_TOO_LONG(true, false, true),
INVALID_TX_EVALUATION_TOO_LONG(true, true, true),
INVALID_TRANSIENT(false, false, true),
INVALID(false, true, false);
private final boolean stop;
private final boolean discard;
private final boolean penalize;
BaseStatus() {
this.stop = false;
this.discard = false;
this.penalize = false;
}
BaseStatus(final boolean stop, final boolean discard) {
BaseStatus(final boolean stop, final boolean discard, final boolean penalize) {
this.stop = stop;
this.discard = discard;
this.penalize = penalize;
}
@Override
public String toString() {
return name() + " (stop=" + stop + ", discard=" + discard + ")";
return name() + " (stop=" + stop + ", discard=" + discard + ", penalize=" + penalize + ")";
}
@Override
@@ -87,6 +98,11 @@ public class TransactionSelectionResult {
public boolean discard() {
return discard;
}
@Override
public boolean penalize() {
return penalize;
}
}
/** The transaction has been selected to be included in the new block */
@@ -105,10 +121,14 @@ public class TransactionSelectionResult {
public static final TransactionSelectionResult BLOCK_SELECTION_TIMEOUT =
new TransactionSelectionResult(BaseStatus.BLOCK_SELECTION_TIMEOUT);
/** Transaction took too much to evaluate */
/** Transaction took too much to evaluate, but it was not invalid */
public static final TransactionSelectionResult TX_EVALUATION_TOO_LONG =
new TransactionSelectionResult(BaseStatus.TX_EVALUATION_TOO_LONG);
/** Transaction took too much to evaluate, and it was invalid */
public static final TransactionSelectionResult INVALID_TX_EVALUATION_TOO_LONG =
new TransactionSelectionResult(BaseStatus.INVALID_TX_EVALUATION_TOO_LONG);
/**
* The transaction has not been selected since too large and the occupancy of the block is enough
* to stop the selection.
@@ -215,6 +235,15 @@ public class TransactionSelectionResult {
return status.discard();
}
/**
* Should the score of this transaction be decremented?
*
* @return yes if the score of this transaction needs to be decremented
*/
public boolean penalize() {
return status.penalize();
}
/**
* Is the candidate transaction selected for block inclusion?
*