mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 13:58:02 -05:00
Merge branch 'main' into zkbesu
# Conflicts: # plugin-api/build.gradle
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: "
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
+ "]";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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">
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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?
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user