@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.blockcreation;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.entry ;
import static org.assertj.core.api.Assertions.entry ;
import static org.awaitility.Awaitility.await ;
import static org.hyperledger.besu.ethereum.core.MiningParameters.Unstable.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME ;
import static org.mockito.ArgumentMatchers.any ;
import static org.mockito.ArgumentMatchers.any ;
import static org.mockito.ArgumentMatchers.anyBoolean ;
import static org.mockito.ArgumentMatchers.anyBoolean ;
import static org.mockito.ArgumentMatchers.eq ;
import static org.mockito.ArgumentMatchers.eq ;
@ -48,6 +50,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty ;
import org.hyperledger.besu.ethereum.core.Difficulty ;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters ;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters ;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues ;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.MutableInitValues ;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters.Unstable ;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider ;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider ;
import org.hyperledger.besu.ethereum.core.MiningParameters ;
import org.hyperledger.besu.ethereum.core.MiningParameters ;
import org.hyperledger.besu.ethereum.core.MutableWorldState ;
import org.hyperledger.besu.ethereum.core.MutableWorldState ;
@ -56,6 +59,7 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt ;
import org.hyperledger.besu.ethereum.core.TransactionReceipt ;
import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule ;
import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSchedule ;
import org.hyperledger.besu.ethereum.eth.manager.EthContext ;
import org.hyperledger.besu.ethereum.eth.manager.EthContext ;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler ;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool ;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool ;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions ;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions ;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor ;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor ;
@ -76,6 +80,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector ;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector ;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory ;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory ;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage ;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage ;
import org.hyperledger.besu.util.number.Percentage ;
import java.math.BigInteger ;
import java.math.BigInteger ;
import java.time.Instant ;
import java.time.Instant ;
@ -83,24 +88,34 @@ import java.util.ArrayList;
import java.util.Arrays ;
import java.util.Arrays ;
import java.util.List ;
import java.util.List ;
import java.util.Optional ;
import java.util.Optional ;
import java.util.concurrent.CompletableFuture ;
import java.util.function.BiFunction ;
import java.util.function.Supplier ;
import java.util.stream.Stream ;
import com.google.common.collect.Lists ;
import com.google.common.collect.Lists ;
import org.apache.tuweni.bytes.Bytes ;
import org.apache.tuweni.bytes.Bytes ;
import org.junit.jupiter.api.BeforeEach ;
import org.junit.jupiter.api.BeforeEach ;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.extension.ExtendWith ;
import org.junit.jupiter.api.extension.ExtendWith ;
import org.junit.jupiter.params.ParameterizedTest ;
import org.junit.jupiter.params.provider.Arguments ;
import org.junit.jupiter.params.provider.MethodSource ;
import org.mockito.Answers ;
import org.mockito.Answers ;
import org.mockito.ArgumentCaptor ;
import org.mockito.ArgumentCaptor ;
import org.mockito.Mock ;
import org.mockito.Mock ;
import org.mockito.junit.jupiter.MockitoExtension ;
import org.mockito.junit.jupiter.MockitoExtension ;
import org.mockito.junit.jupiter.MockitoSettings ;
import org.mockito.junit.jupiter.MockitoSettings ;
import org.mockito.quality.Strictness ;
import org.mockito.quality.Strictness ;
import org.mockito.stubbing.Answer ;
@ExtendWith ( MockitoExtension . class )
@ExtendWith ( MockitoExtension . class )
@MockitoSettings ( strictness = Strictness . LENIENT )
@MockitoSettings ( strictness = Strictness . LENIENT )
public abstract class AbstractBlockTransactionSelectorTest {
public abstract class AbstractBlockTransactionSelectorTest {
protected static final double MIN_OCCUPANCY_80_PERCENT = 0 . 8 ;
protected static final double MIN_OCCUPANCY_80_PERCENT = 0 . 8 ;
protected static final double MIN_OCCUPANCY_100_PERCENT = 1 ;
protected static final double MIN_OCCUPANCY_100_PERCENT = 1 ;
protected static final PluginTransactionSelectorFactory NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY =
( ) - > AllAcceptingTransactionSelector . INSTANCE ;
protected static final BigInteger CHAIN_ID = BigInteger . valueOf ( 42L ) ;
protected static final BigInteger CHAIN_ID = BigInteger . valueOf ( 42L ) ;
protected static final KeyPair keyPair =
protected static final KeyPair keyPair =
SignatureAlgorithmFactory . getInstance ( ) . generateKeyPair ( ) ;
SignatureAlgorithmFactory . getInstance ( ) . generateKeyPair ( ) ;
@ -113,7 +128,11 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected TransactionPool transactionPool ;
protected TransactionPool transactionPool ;
protected MutableWorldState worldState ;
protected MutableWorldState worldState ;
protected ProtocolSchedule protocolSchedule ;
protected ProtocolSchedule protocolSchedule ;
protected MiningParameters miningParameters ;
protected final MiningParameters defaultTestMiningParameters =
createMiningParameters (
Wei . ZERO , MIN_OCCUPANCY_80_PERCENT , DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME ) ;
@Mock protected EthScheduler ethScheduler ;
@Mock ( answer = Answers . RETURNS_DEEP_STUBS )
@Mock ( answer = Answers . RETURNS_DEEP_STUBS )
protected ProtocolContext protocolContext ;
protected ProtocolContext protocolContext ;
@ -150,19 +169,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
when ( protocolContext . getWorldStateArchive ( ) . getMutable ( any ( ) , anyBoolean ( ) ) )
when ( protocolContext . getWorldStateArchive ( ) . getMutable ( any ( ) , anyBoolean ( ) ) )
. thenReturn ( Optional . of ( worldState ) ) ;
. thenReturn ( Optional . of ( worldState ) ) ;
when ( ethContext . getEthPeers ( ) . subscribeConnect ( any ( ) ) ) . thenReturn ( 1L ) ;
when ( ethContext . getEthPeers ( ) . subscribeConnect ( any ( ) ) ) . thenReturn ( 1L ) ;
miningParameters =
when ( ethScheduler . scheduleBlockCreationTask ( any ( Runnable . class ) ) )
ImmutableMiningParameters . builder ( )
. thenAnswer ( invocation - > CompletableFuture . runAsync ( invocation . getArgument ( 0 ) ) ) ;
. mutableInitValues ( MutableInitValues . builder ( ) . minTransactionGasPrice ( Wei . ONE ) . build ( ) )
. build ( ) ;
transactionPool = createTransactionPool ( ) ;
}
}
protected abstract GenesisConfigFile getGenesisConfigFile ( ) ;
protected abstract GenesisConfigFile getGenesisConfigFile ( ) ;
protected abstract ProtocolSchedule createProtocolSchedule ( ) ;
protected abstract ProtocolSchedule createProtocolSchedule ( ) ;
protected abstract TransactionPool createTransactionPool ( ) ;
protected abstract TransactionPool createTransactionPool ( final MiningParameters miningParameters ) ;
private Boolean isCancelled ( ) {
private Boolean isCancelled ( ) {
return false ;
return false ;
@ -198,13 +213,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
mainnetTransactionProcessor ,
mainnetTransactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
@ -216,23 +231,21 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void validPendingTransactionIsIncludedInTheBlock ( ) {
public void validPendingTransactionIsIncludedInTheBlock ( ) {
final Transaction transaction = createTransaction ( 1 , Wei . of ( 7L ) , 100_000 ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction ) ) ;
ensureTransactionIsValid ( transaction , 0 , 5 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 500_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 500_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final Transaction transaction = createTransaction ( 1 , Wei . of ( 7L ) , 100_000 ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction ) ) ;
ensureTransactionIsValid ( transaction , 0 , 5 ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
@ -244,6 +257,18 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void invalidTransactionsAreSkippedButBlockStillFills ( ) {
public void invalidTransactionsAreSkippedButBlockStillFills ( ) {
// The block should fit 4 transactions only
final ProcessableBlockHeader blockHeader = createBlock ( 400_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
blockHeader ,
miningBeneficiary ,
Wei . ZERO ,
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final List < Transaction > transactionsToInject = Lists . newArrayList ( ) ;
final List < Transaction > transactionsToInject = Lists . newArrayList ( ) ;
for ( int i = 0 ; i < 5 ; i + + ) {
for ( int i = 0 ; i < 5 ; i + + ) {
final Transaction tx = createTransaction ( i , Wei . of ( 7 ) , 100_000 ) ;
final Transaction tx = createTransaction ( i , Wei . of ( 7 ) , 100_000 ) ;
@ -256,20 +281,6 @@ public abstract class AbstractBlockTransactionSelectorTest {
}
}
transactionPool . addRemoteTransactions ( transactionsToInject ) ;
transactionPool . addRemoteTransactions ( transactionsToInject ) ;
// The block should fit 4 transactions only
final ProcessableBlockHeader blockHeader = createBlock ( 400_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
createBlockSelector (
transactionProcessor ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final Transaction invalidTx = transactionsToInject . get ( 1 ) ;
final Transaction invalidTx = transactionsToInject . get ( 1 ) ;
@ -288,26 +299,24 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void subsetOfPendingTransactionsIncludedWhenBlockGasLimitHit ( ) {
public void subsetOfPendingTransactionsIncludedWhenBlockGasLimitHit ( ) {
final List < Transaction > transactionsToInject = Lists . newArrayList ( ) ;
for ( int i = 0 ; i < 5 ; i + + ) {
final Transaction tx = createTransaction ( i , Wei . of ( 7 ) , 100_000 ) ;
transactionsToInject . add ( tx ) ;
ensureTransactionIsValid ( tx ) ;
}
transactionPool . addRemoteTransactions ( transactionsToInject ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 301_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 301_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final List < Transaction > transactionsToInject = Lists . newArrayList ( ) ;
for ( int i = 0 ; i < 5 ; i + + ) {
final Transaction tx = createTransaction ( i , Wei . of ( 7 ) , 100_000 ) ;
transactionsToInject . add ( tx ) ;
ensureTransactionIsValid ( tx ) ;
}
transactionPool . addRemoteTransactions ( transactionsToInject ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
@ -332,16 +341,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void transactionTooLargeForBlockDoesNotPreventMoreBeingAddedIfBlockOccupancyNotReached ( ) {
public void transactionTooLargeForBlockDoesNotPreventMoreBeingAddedIfBlockOccupancyNotReached ( ) {
final ProcessableBlockHeader blockHeader = createBlock ( 300_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 300_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
// Add 3 transactions to the Pending Transactions, 79% of block, 100% of block and 10% of block
// Add 3 transactions to the Pending Transactions, 79% of block, 100% of block and 10% of block
// should end up selecting the first and third only.
// should end up selecting the first and third only.
@ -368,16 +376,15 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void transactionSelectionStopsWhenSufficientBlockOccupancyIsReached ( ) {
public void transactionSelectionStopsWhenSufficientBlockOccupancyIsReached ( ) {
final ProcessableBlockHeader blockHeader = createBlock ( 300_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 300_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
// Add 4 transactions to the Pending Transactions 15% (ok), 79% (ok), 25% (too large), 10%
// Add 4 transactions to the Pending Transactions 15% (ok), 79% (ok), 25% (too large), 10%
// (not included, it would fit, however previous transaction was too large and block was
// (not included, it would fit, however previous transaction was too large and block was
@ -409,13 +416,14 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
createMiningParameters (
Wei . ZERO , MIN_OCCUPANCY_100_PERCENT , DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME ) ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_100_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final long minTxGasCost = getGasCalculator ( ) . getMinimumTransactionCost ( ) ;
final long minTxGasCost = getGasCalculator ( ) . getMinimumTransactionCost ( ) ;
@ -467,13 +475,14 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
createMiningParameters (
Wei . ZERO , MIN_OCCUPANCY_100_PERCENT , DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME ) ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_100_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final long minTxGasCost = getGasCalculator ( ) . getMinimumTransactionCost ( ) ;
final long minTxGasCost = getGasCalculator ( ) . getMinimumTransactionCost ( ) ;
@ -519,13 +528,13 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final Transaction validTransaction = createTransaction ( 0 , Wei . of ( 10 ) , 21_000 ) ;
final Transaction validTransaction = createTransaction ( 0 , Wei . of ( 10 ) , 21_000 ) ;
@ -552,6 +561,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void transactionSelectionPluginShouldWork_PreProcessing ( ) {
public void transactionSelectionPluginShouldWork_PreProcessing ( ) {
final ProcessableBlockHeader blockHeader = createBlock ( 300_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 300_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Transaction selected = createTransaction ( 0 , Wei . of ( 10 ) , 21_000 ) ;
final Transaction selected = createTransaction ( 0 , Wei . of ( 10 ) , 21_000 ) ;
ensureTransactionIsValid ( selected , 21_000 , 0 ) ;
ensureTransactionIsValid ( selected , 21_000 , 0 ) ;
@ -584,9 +594,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
}
}
} ;
} ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
miningBeneficiary ,
miningBeneficiary ,
@ -648,7 +658,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelectorWithTxSelPlugin (
createBlockSelectorAndSetupTxPool (
createMiningParameters (
Wei . ZERO , MIN_OCCUPANCY_80_PERCENT , DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME ) ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
miningBeneficiary ,
miningBeneficiary ,
@ -678,15 +690,19 @@ public abstract class AbstractBlockTransactionSelectorTest {
final TransactionInvalidReason invalidReason = TransactionInvalidReason . PLUGIN_TX_VALIDATOR ;
final TransactionInvalidReason invalidReason = TransactionInvalidReason . PLUGIN_TX_VALIDATOR ;
final Transaction invalidTransaction = createTransaction ( 1 , Wei . of ( 10 ) , 21_000 ) ;
final Transaction invalidTransaction = createTransaction ( 1 , Wei . of ( 10 ) , 21_000 ) ;
ensureTransactionIsInvalid ( invalidTransaction , TransactionInvalidReason . PLUGIN_TX_VALIDATOR ) ;
ensureTransactionIsInvalid ( invalidTransaction , TransactionInvalidReason . PLUGIN_TX_VALIDATOR ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction , invalidTransaction ) ) ;
createBlockSelectorWithTxSelPlugin (
final BlockTransactionSelector selector =
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
createBlock ( 300_000 ) ,
createBlock ( 300_000 ) ,
AddressHelpers . ofValue ( 1 ) ,
AddressHelpers . ofValue ( 1 ) ,
Wei . ZERO ,
Wei . ZERO ,
transactionSelectorFactory )
transactionSelectorFactory ) ;
. buildTransactionListForBlock ( ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction , invalidTransaction ) ) ;
selector . buildTransactionListForBlock ( ) ;
ArgumentCaptor < PendingTransaction > argumentCaptor =
ArgumentCaptor < PendingTransaction > argumentCaptor =
ArgumentCaptor . forClass ( PendingTransaction . class ) ;
ArgumentCaptor . forClass ( PendingTransaction . class ) ;
@ -709,21 +725,20 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected ( ) {
public void transactionWithIncorrectNonceRemainsInPoolAndNotSelected ( ) {
final ProcessableBlockHeader blockHeader = createBlock ( 5_000_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 5_000_000 ) ;
final Transaction futureTransaction = createTransaction ( 4 , Wei . of ( 10 ) , 100_000 ) ;
transactionPool . addRemoteTransactions ( List . of ( futureTransaction ) ) ;
ensureTransactionIsInvalid ( futureTransaction , TransactionInvalidReason . NONCE_TOO_HIGH ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
defaultTestMiningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final Transaction futureTransaction = createTransaction ( 4 , Wei . of ( 10 ) , 100_000 ) ;
transactionPool . addRemoteTransactions ( List . of ( futureTransaction ) ) ;
ensureTransactionIsInvalid ( futureTransaction , TransactionInvalidReason . NONCE_TOO_HIGH ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
@ -740,22 +755,25 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void increaseOfMinGasPriceAtRuntimeExcludeTxFromBeingSelected ( ) {
public void increaseOfMinGasPriceAtRuntimeExcludeTxFromBeingSelected ( ) {
final Transaction transaction = createTransaction ( 0 , Wei . of ( 7L ) , 100_000 ) ;
final Transaction transaction = createTransaction ( 0 , Wei . of ( 7L ) , 100_000 ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction ) ) ;
ensureTransactionIsValid ( transaction , 0 , 5 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 500_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 500_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final MiningParameters miningParameters =
ImmutableMiningParameters . builder ( ) . from ( defaultTestMiningParameters ) . build ( ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
miningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction ) ) ;
ensureTransactionIsValid ( transaction , 0 , 5 ) ;
// raise the minGasPrice at runtime from 1 wei to 10 wei
// raise the minGasPrice at runtime from 1 wei to 10 wei
miningParameters . setMinTransactionGasPrice ( Wei . of ( 10 ) ) ;
miningParameters . setMinTransactionGasPrice ( Wei . of ( 10 ) ) ;
@ -774,22 +792,23 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void decreaseOfMinGasPriceAtRuntimeIncludeTxThatWasPreviouslyNotSelected ( ) {
public void decreaseOfMinGasPriceAtRuntimeIncludeTxThatWasPreviouslyNotSelected ( ) {
final Transaction transaction = createTransaction ( 0 , Wei . of ( 7L ) , 100_000 ) ;
final Transaction transaction = createTransaction ( 0 , Wei . of ( 7L ) , 100_000 ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction ) ) ;
final MiningParameters miningParameters =
ImmutableMiningParameters . builder ( ) . from ( defaultTestMiningParameters ) . build ( ) ;
ensureTransactionIsValid ( transaction , 0 , 5 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 500_000 ) ;
final ProcessableBlockHeader blockHeader = createBlock ( 500_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final BlockTransactionSelector selector1 =
final BlockTransactionSelector selector1 =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
miningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
transactionPool . addRemoteTransactions ( List . of ( transaction ) ) ;
ensureTransactionIsValid ( transaction , 0 , 5 ) ;
// raise the minGasPrice at runtime from 1 wei to 10 wei
// raise the minGasPrice at runtime from 1 wei to 10 wei
miningParameters . setMinTransactionGasPrice ( Wei . of ( 10 ) ) ;
miningParameters . setMinTransactionGasPrice ( Wei . of ( 10 ) ) ;
@ -809,12 +828,12 @@ public abstract class AbstractBlockTransactionSelectorTest {
final BlockTransactionSelector selector2 =
final BlockTransactionSelector selector2 =
createBlockSelector (
createBlockSelector (
miningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
miningBeneficiary ,
miningBeneficiary ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_80_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
final TransactionSelectionResults results2 = selector2 . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results2 = selector2 . buildTransactionListForBlock ( ) ;
@ -826,22 +845,25 @@ public abstract class AbstractBlockTransactionSelectorTest {
@Test
@Test
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig ( ) {
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig ( ) {
ProcessableBlockHeader blockHeader = createBlock ( 5_000_000 , Wei . ONE ) ;
ProcessableBlockHeader blockHeader = createBlock ( 5_000_000 , Wei . ONE ) ;
final MiningParameters miningParameters =
ImmutableMiningParameters . builder ( ) . from ( defaultTestMiningParameters ) . build ( ) ;
miningParameters . setMinPriorityFeePerGas ( Wei . of ( 7 ) ) ;
miningParameters . setMinPriorityFeePerGas ( Wei . of ( 7 ) ) ;
final Transaction txSelected = createTransaction ( 1 , Wei . of ( 8 ) , 100_000 ) ;
final Transaction txSelected = createTransaction ( 1 , Wei . of ( 8 ) , 100_000 ) ;
ensureTransactionIsValid ( txSelected ) ;
ensureTransactionIsValid ( txSelected ) ;
// transaction txNotSelected should not be selected
// transaction txNotSelected should not be selected
final Transaction txNotSelected = createTransaction ( 2 , Wei . of ( 7 ) , 100_000 ) ;
final Transaction txNotSelected = createTransaction ( 2 , Wei . of ( 7 ) , 100_000 ) ;
ensureTransactionIsValid ( txNotSelected ) ;
ensureTransactionIsValid ( txNotSelected ) ;
transactionPool . addRemoteTransactions ( List . of ( txSelected , txNotSelected ) ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
createBlockSelector (
createBlockSelectorAndSetupTxPool (
miningParameters ,
transactionProcessor ,
transactionProcessor ,
blockHeader ,
blockHeader ,
Wei . ZERO ,
AddressHelpers . ofValue ( 1 ) ,
AddressHelpers . ofValue ( 1 ) ,
Wei . ZERO ,
Wei . ZERO ,
MIN_OCCUPANCY_100_PERCENT ) ;
NO_PLUGIN_TRANSACTION_SELECTOR_FACTORY ) ;
transactionPool . addRemoteTransactions ( List . of ( txSelected , txNotSelected ) ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
@ -852,41 +874,136 @@ public abstract class AbstractBlockTransactionSelectorTest {
txNotSelected , TransactionSelectionResult . PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN ) ) ;
txNotSelected , TransactionSelectionResult . PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN ) ) ;
}
}
protected BlockTransactionSelector createBlockSelector (
@ParameterizedTest
final MainnetTransactionProcessor transactionProcessor ,
@MethodSource ( "subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver" )
final ProcessableBlockHeader blockHeader ,
public void subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver (
final Wei minGasPrice ,
final boolean isPoa ,
final Address miningBeneficiary ,
final boolean preProcessingTooLate ,
final Wei blobGasPrice ,
final boolean processingTooLate ,
final double minBlockOccupancyRatio ) {
final boolean postProcessingTooLate ) {
final Supplier < Answer < TransactionSelectionResult > > inTime =
( ) - > invocation - > TransactionSelectionResult . SELECTED ;
final BiFunction < Transaction , Long , Answer < TransactionSelectionResult > > tooLate =
( p , t ) - >
invocation - > {
if ( ( ( PendingTransaction ) invocation . getArgument ( 0 ) ) . getTransaction ( ) . equals ( p ) ) {
Thread . sleep ( t ) ;
}
return TransactionSelectionResult . SELECTED ;
} ;
final ProcessableBlockHeader blockHeader = createBlock ( 301_000 ) ;
final Address miningBeneficiary = AddressHelpers . ofValue ( 1 ) ;
final int poaMinBlockTime = 1 ;
final long blockTxsSelectionMaxTime = 750 ;
final long longProcessingTxTime = 500 ;
final List < Transaction > transactionsToInject = new ArrayList < > ( 3 ) ;
for ( int i = 0 ; i < 2 ; i + + ) {
final Transaction tx = createTransaction ( i , Wei . of ( 7 ) , 100_000 ) ;
transactionsToInject . add ( tx ) ;
ensureTransactionIsValid ( tx ) ;
}
final Transaction lateTx = createTransaction ( 2 , Wei . of ( 7 ) , 100_000 ) ;
transactionsToInject . add ( lateTx ) ;
ensureTransactionIsValid (
lateTx , 0 , 0 , processingTooLate ? blockTxsSelectionMaxTime + longProcessingTxTime : 0 ) ;
PluginTransactionSelector transactionSelector = mock ( PluginTransactionSelector . class ) ;
when ( transactionSelector . evaluateTransactionPreProcessing ( any ( ) ) )
. thenAnswer (
preProcessingTooLate
? inTime . get ( )
: tooLate . apply ( lateTx , blockTxsSelectionMaxTime + longProcessingTxTime ) ) ;
when ( transactionSelector . evaluateTransactionPostProcessing ( any ( ) , any ( ) ) )
. thenAnswer (
postProcessingTooLate
? inTime . get ( )
: tooLate . apply ( lateTx , blockTxsSelectionMaxTime + longProcessingTxTime ) ) ;
final PluginTransactionSelectorFactory transactionSelectorFactory =
mock ( PluginTransactionSelectorFactory . class ) ;
when ( transactionSelectorFactory . create ( ) ) . thenReturn ( transactionSelector ) ;
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
new BlockTransactionSelector (
createBlockSelectorAndSetupTxPool (
miningParameters
isPoa
. setMinTransactionGasPrice ( minGasPrice )
? createMiningParameters (
. setMinBlockOccupancyRatio ( minBlockOccupancyRatio ) ,
Wei . ZERO , MIN_OCCUPANCY_100_PERCENT , poaMinBlockTime , Percentage . fromInt ( 75 ) )
: createMiningParameters (
Wei . ZERO , MIN_OCCUPANCY_100_PERCENT , blockTxsSelectionMaxTime ) ,
transactionProcessor ,
transactionProcessor ,
blockchain ,
worldState ,
transactionPool ,
blockHeader ,
blockHeader ,
this : : createReceipt ,
this : : isCancelled ,
miningBeneficiary ,
miningBeneficiary ,
blobGasPrice ,
Wei . ZERO ,
getFeeMarket ( ) ,
transactionSelectorFactory ) ;
new LondonGasCalculator ( ) ,
GasLimitCalculator . constant ( ) ,
AllAcceptingTransactionSelector . INSTANCE ) ;
return selector ;
transactionPool . addRemoteTransactions ( transactionsToInject ) ;
final TransactionSelectionResults results = selector . buildTransactionListForBlock ( ) ;
// third tx is not selected, even if it could fit in the block,
// since the selection time was over
assertThat ( results . getSelectedTransactions ( ) . size ( ) ) . isEqualTo ( 2 ) ;
assertThat ( results . getSelectedTransactions ( ) . containsAll ( transactionsToInject . subList ( 0 , 2 ) ) )
. isTrue ( ) ;
assertThat ( results . getReceipts ( ) . size ( ) ) . isEqualTo ( 2 ) ;
assertThat ( results . getCumulativeGasUsed ( ) ) . isEqualTo ( 200_000 ) ;
// Ensure receipts have the correct cumulative gas
assertThat ( results . getReceipts ( ) . get ( 0 ) . getCumulativeGasUsed ( ) ) . isEqualTo ( 100_000 ) ;
assertThat ( results . getReceipts ( ) . get ( 1 ) . getCumulativeGasUsed ( ) ) . isEqualTo ( 200_000 ) ;
// given enough time we can check the not selected tx
await ( ) . until ( ( ) - > ! results . getNotSelectedTransactions ( ) . isEmpty ( ) ) ;
assertThat ( results . getNotSelectedTransactions ( ) )
. containsOnly ( entry ( lateTx , TransactionSelectionResult . BLOCK_SELECTION_TIMEOUT ) ) ;
}
private static Stream < Arguments >
subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver ( ) {
return Stream . of (
Arguments . of ( false , true , false , false ) ,
Arguments . of ( false , false , true , false ) ,
Arguments . of ( false , false , false , true ) ,
Arguments . of ( true , true , false , false ) ,
Arguments . of ( true , false , true , false ) ,
Arguments . of ( true , false , false , true ) ) ;
}
protected BlockTransactionSelector createBlockSelectorAndSetupTxPool (
final MiningParameters miningParameters ,
final MainnetTransactionProcessor transactionProcessor ,
final ProcessableBlockHeader blockHeader ,
final Address miningBeneficiary ,
final Wei blobGasPrice ,
final PluginTransactionSelectorFactory transactionSelectorFactory ) {
transactionPool = createTransactionPool ( miningParameters ) ;
return createBlockSelector (
miningParameters ,
transactionProcessor ,
blockHeader ,
miningBeneficiary ,
blobGasPrice ,
transactionSelectorFactory ) ;
}
}
protected BlockTransactionSelector createBlockSelectorWithTxSelPlugin (
protected BlockTransactionSelector createBlockSelector (
final MiningParameters miningParameters ,
final MainnetTransactionProcessor transactionProcessor ,
final MainnetTransactionProcessor transactionProcessor ,
final ProcessableBlockHeader blockHeader ,
final ProcessableBlockHeader blockHeader ,
final Address miningBeneficiary ,
final Address miningBeneficiary ,
final Wei blobGasPrice ,
final Wei blobGasPrice ,
final PluginTransactionSelectorFactory transactionSelectorFactory ) {
final PluginTransactionSelectorFactory transactionSelectorFactory ) {
final BlockTransactionSelector selector =
final BlockTransactionSelector selector =
new BlockTransactionSelector (
new BlockTransactionSelector (
miningParameters ,
miningParameters ,
@ -902,7 +1019,8 @@ public abstract class AbstractBlockTransactionSelectorTest {
getFeeMarket ( ) ,
getFeeMarket ( ) ,
new LondonGasCalculator ( ) ,
new LondonGasCalculator ( ) ,
GasLimitCalculator . constant ( ) ,
GasLimitCalculator . constant ( ) ,
transactionSelectorFactory . create ( ) ) ;
transactionSelectorFactory . create ( ) ,
ethScheduler ) ;
return selector ;
return selector ;
}
}
@ -965,15 +1083,28 @@ public abstract class AbstractBlockTransactionSelectorTest {
protected void ensureTransactionIsValid (
protected void ensureTransactionIsValid (
final Transaction tx , final long gasUsedByTransaction , final long gasRemaining ) {
final Transaction tx , final long gasUsedByTransaction , final long gasRemaining ) {
ensureTransactionIsValid ( tx , gasUsedByTransaction , gasRemaining , 0 ) ;
}
protected void ensureTransactionIsValid (
final Transaction tx ,
final long gasUsedByTransaction ,
final long gasRemaining ,
final long processingTime ) {
when ( transactionProcessor . processTransaction (
when ( transactionProcessor . processTransaction (
any ( ) , any ( ) , any ( ) , eq ( tx ) , any ( ) , any ( ) , any ( ) , anyBoolean ( ) , any ( ) , any ( ) ) )
any ( ) , any ( ) , any ( ) , eq ( tx ) , any ( ) , any ( ) , any ( ) , anyBoolean ( ) , any ( ) , any ( ) ) )
. thenReturn (
. thenAnswer (
TransactionProcessingResult . successful (
invocation - > {
new ArrayList < > ( ) ,
if ( processingTime > 0 ) {
gasUsedByTransaction ,
Thread . sleep ( processingTime ) ;
gasRemaining ,
}
Bytes . EMPTY ,
return TransactionProcessingResult . successful (
ValidationResult . valid ( ) ) ) ;
new ArrayList < > ( ) ,
gasUsedByTransaction ,
gasRemaining ,
Bytes . EMPTY ,
ValidationResult . valid ( ) ) ;
} ) ;
}
}
protected void ensureTransactionIsInvalid (
protected void ensureTransactionIsInvalid (
@ -986,4 +1117,35 @@ public abstract class AbstractBlockTransactionSelectorTest {
private BlockHeader blockHeader ( final long number ) {
private BlockHeader blockHeader ( final long number ) {
return new BlockHeaderTestFixture ( ) . number ( number ) . buildHeader ( ) ;
return new BlockHeaderTestFixture ( ) . number ( number ) . buildHeader ( ) ;
}
}
protected MiningParameters createMiningParameters (
final Wei minGasPrice , final double minBlockOccupancyRatio , final long txsSelectionMaxTime ) {
return ImmutableMiningParameters . builder ( )
. mutableInitValues (
MutableInitValues . builder ( )
. minTransactionGasPrice ( minGasPrice )
. minBlockOccupancyRatio ( minBlockOccupancyRatio )
. build ( ) )
. unstable ( Unstable . builder ( ) . nonPoaBlockTxsSelectionMaxTime ( txsSelectionMaxTime ) . build ( ) )
. build ( ) ;
}
protected MiningParameters createMiningParameters (
final Wei minGasPrice ,
final double minBlockOccupancyRatio ,
final int minBlockTime ,
final Percentage minBlockTimePercentage ) {
return ImmutableMiningParameters . builder ( )
. mutableInitValues (
MutableInitValues . builder ( )
. minTransactionGasPrice ( minGasPrice )
. minBlockOccupancyRatio ( minBlockOccupancyRatio )
. build ( ) )
. unstable (
Unstable . builder ( )
. minBlockTime ( minBlockTime )
. poaBlockTxsSelectionMaxTime ( minBlockTimePercentage )
. build ( ) )
. build ( ) ;
}
}
}