Refactor transaction pool tests (#4632)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/4659/head
Fabio Di Fabio 2 years ago committed by GitHub
parent b181f99940
commit fe0b628e0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java
  2. 702
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  3. 263
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLegacyTest.java
  4. 221
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLondonTest.java
  5. 1242
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java

@ -46,6 +46,8 @@ public class TransactionTestFixture {
private Optional<Wei> maxPriorityFeePerGas = Optional.empty();
private Optional<Wei> maxFeePerGas = Optional.empty();
private Optional<BigInteger> v = Optional.empty();
public Transaction createTransaction(final KeyPair keys) {
final Transaction.Builder builder = Transaction.builder();
builder
@ -63,6 +65,8 @@ public class TransactionTestFixture {
maxPriorityFeePerGas.ifPresent(builder::maxPriorityFeePerGas);
maxFeePerGas.ifPresent(builder::maxFeePerGas);
v.ifPresent(builder::v);
return builder.signAndBuild(keys);
}
@ -120,4 +124,9 @@ public class TransactionTestFixture {
this.maxFeePerGas = maxFeePerGas;
return this;
}
public TransactionTestFixture v(final Optional<BigInteger> v) {
this.v = v;
return this;
}
}

@ -0,0 +1,702 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.mainnet.ValidationResult.valid;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXCEEDS_BLOCK_GAS_LIMIT;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_TOO_LOW;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_LOW;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.TRANSACTION_REPLACEMENT_UNDERPRICED;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.TX_FEECAP_EXCEEDED;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@SuppressWarnings("unchecked")
@RunWith(MockitoJUnitRunner.class)
public abstract class AbstractTransactionPoolTest {
protected static final int MAX_TRANSACTIONS = 5;
protected static final KeyPair KEY_PAIR1 =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
private static final KeyPair KEY_PAIR2 =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
@Mock protected MainnetTransactionValidator transactionValidator;
@Mock protected PendingTransactionListener listener;
@Mock protected MiningParameters miningParameters;
@Mock protected TransactionsMessageSender transactionsMessageSender;
@Mock protected NewPooledTransactionHashesMessageSender newPooledTransactionHashesMessageSender;
@Mock protected ProtocolSpec protocolSpec;
protected ProtocolSchedule protocolSchedule;
protected final MetricsSystem metricsSystem = new NoOpMetricsSystem();
protected MutableBlockchain blockchain;
private TransactionBroadcaster transactionBroadcaster;
private AbstractPendingTransactionsSorter transactions;
private final Transaction transaction1 = createTransaction(1);
private final Transaction transaction2 = createTransaction(2);
private final Transaction transactionOtherSender = createTransaction(1, KEY_PAIR2);
private ExecutionContextTestFixture executionContext;
protected ProtocolContext protocolContext;
protected TransactionPool transactionPool;
protected long blockGasLimit;
protected EthProtocolManager ethProtocolManager;
private EthContext ethContext;
private PeerTransactionTracker peerTransactionTracker;
private ArgumentCaptor<Runnable> syncTaskCapture;
protected abstract AbstractPendingTransactionsSorter createPendingTransactionsSorter();
protected abstract ExecutionContextTestFixture createExecutionContextTestFixture();
protected abstract FeeMarket getFeeMarket();
@Before
public void setUp() {
executionContext = createExecutionContextTestFixture();
protocolContext = executionContext.getProtocolContext();
blockchain = executionContext.getBlockchain();
transactions = spy(createPendingTransactionsSorter());
when(protocolSpec.getTransactionValidator()).thenReturn(transactionValidator);
when(protocolSpec.getFeeMarket()).thenReturn(getFeeMarket());
protocolSchedule = spy(executionContext.getProtocolSchedule());
doReturn(protocolSpec).when(protocolSchedule).getByBlockNumber(anyLong());
blockGasLimit = blockchain.getChainHeadBlock().getHeader().getGasLimit();
ethProtocolManager = EthProtocolManagerTestUtil.create();
ethContext = spy(ethProtocolManager.ethContext());
final EthScheduler ethScheduler = mock(EthScheduler.class);
syncTaskCapture = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(ethScheduler).scheduleSyncWorkerTask(syncTaskCapture.capture());
doReturn(ethScheduler).when(ethContext).getScheduler();
peerTransactionTracker = new PeerTransactionTracker();
transactionBroadcaster =
spy(
new TransactionBroadcaster(
ethContext,
transactions,
peerTransactionTracker,
transactionsMessageSender,
newPooledTransactionHashesMessageSender));
transactionPool = createTransactionPool();
blockchain.observeBlockAdded(transactionPool);
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.of(2));
}
protected TransactionPool createTransactionPool() {
return createTransactionPool(b -> {});
}
protected TransactionPool createTransactionPool(
final Consumer<ImmutableTransactionPoolConfiguration.Builder> configConsumer) {
final ImmutableTransactionPoolConfiguration.Builder configBuilder =
ImmutableTransactionPoolConfiguration.builder();
configConsumer.accept(configBuilder);
final TransactionPoolConfiguration config = configBuilder.build();
return new TransactionPool(
transactions,
protocolSchedule,
protocolContext,
transactionBroadcaster,
ethContext,
miningParameters,
metricsSystem,
config);
}
@Test
public void localTransactionHappyPath() {
final Transaction transaction = createTransaction(0);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
}
@Test
public void shouldReturnExclusivelyLocalTransactionsWhenAppropriate() {
final Transaction localTransaction0 = createTransaction(0);
givenTransactionIsValid(localTransaction0);
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
assertLocalTransactionValid(localTransaction0);
assertRemoteTransactionValid(transaction1);
assertRemoteTransactionValid(transaction2);
assertThat(transactions.size()).isEqualTo(3);
List<Transaction> localTransactions = transactions.getLocalTransactions();
assertThat(localTransactions.size()).isEqualTo(1);
}
@Test
public void shouldRemoveTransactionsFromPendingListWhenIncludedInBlockOnchain() {
transactions.addRemoteTransaction(transaction1, Optional.empty());
assertTransactionPending(transaction1);
appendBlock(transaction1);
assertTransactionNotPending(transaction1);
}
@Test
public void shouldRemoveMultipleTransactionsAddedInOneBlock() {
transactions.addRemoteTransaction(transaction1, Optional.empty());
transactions.addRemoteTransaction(transaction2, Optional.empty());
appendBlock(transaction1, transaction2);
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transaction2);
assertThat(transactions.size()).isZero();
}
@Test
public void shouldIgnoreUnknownTransactionsThatAreAddedInABlock() {
transactions.addRemoteTransaction(transaction1, Optional.empty());
appendBlock(transaction1, transaction2);
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transaction2);
assertThat(transactions.size()).isZero();
}
@Test
public void shouldNotRemovePendingTransactionsWhenABlockAddedToAFork() {
transactions.addRemoteTransaction(transaction1, Optional.empty());
final BlockHeader commonParent = getHeaderForCurrentChainHead();
final Block canonicalHead = appendBlock(Difficulty.of(1000), commonParent);
appendBlock(Difficulty.ONE, commonParent, transaction1);
verifyChainHeadIs(canonicalHead);
assertTransactionPending(transaction1);
}
@Test
public void shouldRemovePendingTransactionsFromAllBlocksOnAForkWhenItBecomesTheCanonicalChain() {
transactions.addRemoteTransaction(transaction1, Optional.empty());
transactions.addRemoteTransaction(transaction2, Optional.empty());
final BlockHeader commonParent = getHeaderForCurrentChainHead();
final Block originalChainHead = appendBlock(Difficulty.of(1000), commonParent);
final Block forkBlock1 = appendBlock(Difficulty.ONE, commonParent, transaction1);
verifyChainHeadIs(originalChainHead);
final Block forkBlock2 = appendBlock(Difficulty.of(2000), forkBlock1.getHeader(), transaction2);
verifyChainHeadIs(forkBlock2);
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transaction2);
}
@Test
public void shouldReAddTransactionsFromThePreviousCanonicalHeadWhenAReorgOccurs() {
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transactionOtherSender);
transactions.addLocalTransaction(transaction1, Optional.empty());
transactions.addRemoteTransaction(transactionOtherSender, Optional.empty());
final BlockHeader commonParent = getHeaderForCurrentChainHead();
final Block originalFork1 = appendBlock(Difficulty.of(1000), commonParent, transaction1);
final Block originalFork2 =
appendBlock(Difficulty.ONE, originalFork1.getHeader(), transactionOtherSender);
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transactionOtherSender);
assertThat(transactions.getLocalTransactions()).isEmpty();
final Block reorgFork1 = appendBlock(Difficulty.ONE, commonParent);
verifyChainHeadIs(originalFork2);
transactions.subscribePendingTransactions(listener);
final Block reorgFork2 = appendBlock(Difficulty.of(2000), reorgFork1.getHeader());
verifyChainHeadIs(reorgFork2);
assertTransactionPending(transaction1);
assertTransactionPending(transactionOtherSender);
assertThat(transactions.getLocalTransactions()).contains(transaction1);
assertThat(transactions.getLocalTransactions()).doesNotContain(transactionOtherSender);
verify(listener).onTransactionAdded(transaction1);
verify(listener).onTransactionAdded(transactionOtherSender);
verifyNoMoreInteractions(listener);
}
@Test
public void shouldNotReAddTransactionsThatAreInBothForksWhenReorgHappens() {
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
transactions.addRemoteTransaction(transaction1, Optional.empty());
transactions.addRemoteTransaction(transaction2, Optional.empty());
final BlockHeader commonParent = getHeaderForCurrentChainHead();
final Block originalFork1 = appendBlock(Difficulty.of(1000), commonParent, transaction1);
final Block originalFork2 =
appendBlock(Difficulty.ONE, originalFork1.getHeader(), transaction2);
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transaction2);
final Block reorgFork1 = appendBlock(Difficulty.ONE, commonParent, transaction1);
verifyChainHeadIs(originalFork2);
final Block reorgFork2 = appendBlock(Difficulty.of(2000), reorgFork1.getHeader());
verifyChainHeadIs(reorgFork2);
assertTransactionNotPending(transaction1);
assertTransactionPending(transaction2);
}
@Test
public void addLocalTransaction_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured() {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransaction(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
}
@Test
public void addRemoteTransactions_strictReplayProtectionOn_txWithChainId_chainIdIsConfigured() {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransaction(1);
givenTransactionIsValid(tx);
assertRemoteTransactionValid(tx);
}
@Test
public void shouldNotAddRemoteTransactionsWhenGasPriceBelowMinimum() {
final Transaction transaction = createTransaction(1, Wei.ONE);
transactionPool.addRemoteTransactions(singletonList(transaction));
assertTransactionNotPending(transaction);
verifyNoInteractions(transactionValidator); // Reject before validation
}
@Test
public void shouldNotAddRemoteTransactionsWhenThereIsAnLowestInvalidNonceForTheSender() {
givenTransactionIsValid(transaction2);
when(transactionValidator.validate(eq(transaction1), any(Optional.class), any()))
.thenReturn(ValidationResult.invalid(NONCE_TOO_LOW));
transactionPool.addRemoteTransactions(asList(transaction1, transaction2));
assertTransactionNotPending(transaction1);
assertTransactionNotPending(transaction2);
verify(transactionBroadcaster, never()).onTransactionsAdded(singletonList(transaction2));
}
@Test
public void shouldNotAddRemoteTransactionsThatAreInvalidAccordingToStateDependentChecks() {
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
when(transactionValidator.validateForSender(
eq(transaction2), eq(null), any(TransactionValidationParams.class)))
.thenReturn(ValidationResult.invalid(NONCE_TOO_LOW));
transactionPool.addRemoteTransactions(asList(transaction1, transaction2));
assertTransactionPending(transaction1);
assertTransactionNotPending(transaction2);
verify(transactionBroadcaster).onTransactionsAdded(singletonList(transaction1));
verify(transactionValidator).validate(eq(transaction1), any(Optional.class), any());
verify(transactionValidator)
.validateForSender(eq(transaction1), eq(null), any(TransactionValidationParams.class));
verify(transactionValidator).validate(eq(transaction2), any(Optional.class), any());
verify(transactionValidator).validateForSender(eq(transaction2), any(), any());
verify(transactionValidator, atLeastOnce()).getGoQuorumCompatibilityMode();
verifyNoMoreInteractions(transactionValidator);
}
@Test
public void shouldAllowSequenceOfTransactionsWithIncreasingNonceFromSameSender() {
final Transaction transaction1 = createTransaction(1);
final Transaction transaction2 = createTransaction(2);
final Transaction transaction3 = createTransaction(3);
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
givenTransactionIsValid(transaction3);
assertLocalTransactionValid(transaction1);
assertLocalTransactionValid(transaction2);
assertLocalTransactionValid(transaction3);
}
@Test
public void
shouldAllowSequenceOfTransactionsWithIncreasingNonceFromSameSenderWhenSentInBatchOutOfOrder() {
final Transaction transaction1 = createTransaction(1);
final Transaction transaction2 = createTransaction(2);
final Transaction transaction3 = createTransaction(3);
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
givenTransactionIsValid(transaction3);
assertRemoteTransactionValid(transaction3);
assertRemoteTransactionValid(transaction1);
assertRemoteTransactionValid(transaction2);
}
@Test
public void shouldDiscardRemoteTransactionThatAlreadyExistsBeforeValidation() {
doReturn(true).when(transactions).containsTransaction(transaction1.getHash());
transactionPool.addRemoteTransactions(singletonList(transaction1));
verify(transactions).containsTransaction(transaction1.getHash());
verifyNoInteractions(transactionValidator);
verifyNoMoreInteractions(transactions);
}
@Test
public void shouldNotNotifyBatchListenerWhenRemoteTransactionDoesNotReplaceExisting() {
final Transaction transaction1 = createTransaction(1, Wei.of(100));
final Transaction transaction2 = createTransaction(1, Wei.of(50));
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
assertRemoteTransactionValid(transaction1);
assertRemoteTransactionInvalid(transaction2);
}
@Test
public void shouldNotNotifyBatchListenerWhenLocalTransactionDoesNotReplaceExisting() {
final Transaction transaction1 = createTransaction(1, Wei.of(10));
final Transaction transaction2 = createTransaction(1, Wei.of(9));
givenTransactionIsValid(transaction1);
givenTransactionIsValid(transaction2);
assertLocalTransactionValid(transaction1);
assertLocalTransactionInvalid(transaction2, TRANSACTION_REPLACEMENT_UNDERPRICED);
}
@Test
public void shouldRejectLocalTransactionsWhereGasLimitExceedBlockGasLimit() {
final Transaction transaction1 =
createBaseTransaction(0).gasLimit(blockGasLimit + 1).createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1);
assertLocalTransactionInvalid(transaction1, EXCEEDS_BLOCK_GAS_LIMIT);
}
@Test
public void shouldRejectRemoteTransactionsWhereGasLimitExceedBlockGasLimit() {
final Transaction transaction1 =
createBaseTransaction(0).gasLimit(blockGasLimit + 1).createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction1);
assertRemoteTransactionInvalid(transaction1);
}
@Test
public void shouldRejectRemoteTransactionsAnInvalidTransactionWithLowerNonceExists() {
final Transaction invalidTx =
createBaseTransaction(0).gasLimit(blockGasLimit + 1).createTransaction(KEY_PAIR1);
final Transaction nextTx = createTransaction(1);
givenTransactionIsValid(invalidTx);
givenTransactionIsValid(nextTx);
assertRemoteTransactionInvalid(invalidTx);
assertRemoteTransactionInvalid(nextTx);
}
@Test
public void shouldAcceptLocalTransactionsEvenIfAnInvalidTransactionWithLowerNonceExists() {
final Transaction invalidTx =
createBaseTransaction(0).gasLimit(blockGasLimit + 1).createTransaction(KEY_PAIR1);
final Transaction nextTx = createBaseTransaction(1).gasLimit(1).createTransaction(KEY_PAIR1);
givenTransactionIsValid(invalidTx);
givenTransactionIsValid(nextTx);
assertLocalTransactionInvalid(invalidTx, EXCEEDS_BLOCK_GAS_LIMIT);
assertLocalTransactionValid(nextTx);
}
@Test
public void shouldRejectLocalTransactionsWhenNonceTooFarInFuture() {
final Transaction transaction1 = createTransaction(Integer.MAX_VALUE);
givenTransactionIsValid(transaction1);
assertLocalTransactionInvalid(transaction1, NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER);
}
@Test
public void shouldNotNotifyBatchListenerIfNoTransactionsAreAdded() {
transactionPool.addRemoteTransactions(emptyList());
verifyNoInteractions(transactionBroadcaster);
}
@Test
public void shouldSendPooledTransactionHashesIfPeerSupportsEth65() {
EthPeer peer = mock(EthPeer.class);
when(peer.hasSupportForMessage(EthPV65.NEW_POOLED_TRANSACTION_HASHES)).thenReturn(true);
givenTransactionIsValid(transaction1);
transactionPool.addLocalTransaction(transaction1);
transactionPool.handleConnect(peer);
syncTaskCapture.getValue().run();
verify(newPooledTransactionHashesMessageSender).sendTransactionHashesToPeer(peer);
}
@Test
public void shouldSendFullTransactionsIfPeerDoesNotSupportEth65() {
EthPeer peer = mock(EthPeer.class);
when(peer.hasSupportForMessage(EthPV65.NEW_POOLED_TRANSACTION_HASHES)).thenReturn(false);
givenTransactionIsValid(transaction1);
transactionPool.addLocalTransaction(transaction1);
transactionPool.handleConnect(peer);
syncTaskCapture.getValue().run();
verify(transactionsMessageSender).sendTransactionsToPeer(peer);
}
@Test
public void shouldSendFullTransactionPoolToNewlyConnectedPeer() {
final Transaction transactionLocal = createTransaction(1);
final Transaction transactionRemote = createTransaction(2);
givenTransactionIsValid(transactionLocal);
givenTransactionIsValid(transactionRemote);
transactionPool.addLocalTransaction(transactionLocal);
transactionPool.addRemoteTransactions(Collections.singletonList(transactionRemote));
RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
Set<Transaction> transactionsToSendToPeer =
peerTransactionTracker.claimTransactionsToSendToPeer(peer.getEthPeer());
assertThat(transactionsToSendToPeer).contains(transactionLocal, transactionRemote);
}
@Test
public void shouldCallValidatorWithExpectedValidationParameters() {
final ArgumentCaptor<TransactionValidationParams> txValidationParamCaptor =
ArgumentCaptor.forClass(TransactionValidationParams.class);
when(transactionValidator.validate(eq(transaction1), any(Optional.class), any()))
.thenReturn(valid());
when(transactionValidator.validateForSender(any(), any(), txValidationParamCaptor.capture()))
.thenReturn(valid());
final TransactionValidationParams expectedValidationParams =
TransactionValidationParams.transactionPool();
transactionPool.addLocalTransaction(transaction1);
assertThat(txValidationParamCaptor.getValue())
.usingRecursiveComparison()
.isEqualTo(expectedValidationParams);
}
@Test
public void shouldIgnoreFeeCapIfSetZero() {
final Wei twoEthers = Wei.fromEth(2);
transactionPool = createTransactionPool(b -> b.txFeeCap(Wei.ZERO));
final Transaction transaction = createTransaction(1, twoEthers.add(Wei.of(1)));
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
}
@Test
public void shouldRejectLocalTransactionIfFeeCapExceeded() {
final Wei twoEthers = Wei.fromEth(2);
transactionPool = createTransactionPool(b -> b.txFeeCap(twoEthers));
final Transaction transactionLocal = createTransaction(1, twoEthers.add(1));
givenTransactionIsValid(transactionLocal);
assertLocalTransactionInvalid(transactionLocal, TX_FEECAP_EXCEEDED);
}
@Test
public void shouldRejectZeroGasPriceTransactionWhenNotMining() {
when(miningParameters.isMiningEnabled()).thenReturn(false);
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionInvalid(transaction, GAS_PRICE_TOO_LOW);
}
private void assertTransactionPending(final Transaction t) {
assertThat(transactions.getTransactionByHash(t.getHash())).contains(t);
}
private void assertTransactionNotPending(final Transaction transaction) {
assertThat(transactions.getTransactionByHash(transaction.getHash())).isEmpty();
}
private void verifyChainHeadIs(final Block forkBlock2) {
assertThat(blockchain.getChainHeadHash()).isEqualTo(forkBlock2.getHash());
}
private void appendBlock(final Transaction... transactionsToAdd) {
appendBlock(Difficulty.ONE, getHeaderForCurrentChainHead(), transactionsToAdd);
}
private BlockHeader getHeaderForCurrentChainHead() {
return blockchain.getBlockHeader(blockchain.getChainHeadHash()).get();
}
protected abstract Block appendBlock(
final Difficulty difficulty,
final BlockHeader parentBlock,
final Transaction... transactionsToAdd);
protected abstract Transaction createTransaction(
final int transactionNumber, final Optional<BigInteger> maybeChainId);
protected abstract Transaction createTransaction(final int transactionNumber, final Wei maxPrice);
protected abstract TransactionTestFixture createBaseTransaction(final int transactionNumber);
private Transaction createTransaction(final int transactionNumber) {
return createTransaction(transactionNumber, Optional.of(BigInteger.ONE));
}
private Transaction createTransaction(final int transactionNumber, final KeyPair keyPair) {
return createBaseTransaction(transactionNumber).createTransaction(keyPair);
}
protected void protocolSupportsTxReplayProtection(
final long chainId, final boolean isSupportedAtCurrentBlock) {
when(protocolSpec.isReplayProtectionSupported()).thenReturn(isSupportedAtCurrentBlock);
when(protocolSchedule.getChainId()).thenReturn(Optional.of(BigInteger.valueOf(chainId)));
}
protected void givenTransactionIsValid(final Transaction transaction) {
when(transactionValidator.validate(eq(transaction), any(Optional.class), any()))
.thenReturn(valid());
when(transactionValidator.validateForSender(
eq(transaction), nullable(Account.class), any(TransactionValidationParams.class)))
.thenReturn(valid());
}
protected void assertLocalTransactionInvalid(
final Transaction tx, final TransactionInvalidReason invalidReason) {
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(tx);
assertThat(result.isValid()).isFalse();
assertThat(result.getInvalidReason()).isEqualTo(invalidReason);
assertTransactionNotPending(tx);
verify(transactionBroadcaster, never()).onTransactionsAdded(singletonList(tx));
}
protected void assertLocalTransactionValid(final Transaction tx) {
final ValidationResult<TransactionInvalidReason> result =
transactionPool.addLocalTransaction(tx);
assertThat(result.isValid()).isTrue();
assertTransactionPending(tx);
verify(transactionBroadcaster).onTransactionsAdded(singletonList(tx));
assertThat(transactions.getLocalTransactions()).contains(tx);
}
protected void assertRemoteTransactionValid(final Transaction tx) {
transactionPool.addRemoteTransactions(List.of(tx));
verify(transactionBroadcaster).onTransactionsAdded(singletonList(tx));
assertTransactionPending(tx);
assertThat(transactions.getLocalTransactions()).doesNotContain(tx);
}
protected void assertRemoteTransactionInvalid(final Transaction tx) {
transactionPool.addRemoteTransactions(List.of(tx));
verify(transactionBroadcaster, never()).onTransactionsAdded(singletonList(tx));
assertTransactionNotPending(tx);
}
}

@ -0,0 +1,263 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_TRANSACTION_FORMAT;
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.REPLAY_PROTECTED_SIGNATURE_REQUIRED;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.testutil.TestClock;
import java.math.BigInteger;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
@SuppressWarnings("unchecked")
@RunWith(MockitoJUnitRunner.class)
public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
@Override
protected AbstractPendingTransactionsSorter createPendingTransactionsSorter() {
return new GasPricePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1)
.build(),
TestClock.system(ZoneId.systemDefault()),
metricsSystem,
protocolContext.getBlockchain()::getChainHeadHeader);
}
@Override
protected Transaction createTransaction(
final int transactionNumber, final Optional<BigInteger> maybeChainId) {
return createBaseTransaction(transactionNumber)
.chainId(maybeChainId)
.createTransaction(KEY_PAIR1);
}
@Override
protected Transaction createTransaction(final int transactionNumber, final Wei maxPrice) {
return createBaseTransaction(transactionNumber).gasPrice(maxPrice).createTransaction(KEY_PAIR1);
}
@Override
protected TransactionTestFixture createBaseTransaction(final int transactionNumber) {
return new TransactionTestFixture()
.nonce(transactionNumber)
.gasLimit(blockGasLimit)
.type(TransactionType.FRONTIER);
}
@Override
protected ExecutionContextTestFixture createExecutionContextTestFixture() {
return ExecutionContextTestFixture.create();
}
@Override
protected FeeMarket getFeeMarket() {
return FeeMarket.legacy();
}
@Override
protected Block appendBlock(
final Difficulty difficulty,
final BlockHeader parentBlock,
final Transaction... transactionsToAdd) {
final List<Transaction> transactionList = asList(transactionsToAdd);
final Block block =
new Block(
new BlockHeaderTestFixture()
.difficulty(difficulty)
.gasLimit(parentBlock.getGasLimit())
.parentHash(parentBlock.getHash())
.number(parentBlock.getNumber() + 1)
.buildHeader(),
new BlockBody(transactionList, emptyList()));
final List<TransactionReceipt> transactionReceipts =
transactionList.stream()
.map(transaction -> new TransactionReceipt(1, 1, emptyList(), Optional.empty()))
.collect(toList());
blockchain.appendBlock(block, transactionReceipts);
return block;
}
@Test
public void
addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsConfigured_protectionNotSupportedAtCurrentBlock() {
protocolSupportsTxReplayProtection(1337, false);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
}
@Test
public void
addRemoteTransactions_strictReplayProtectionOff_txWithoutChainId_chainIdIsConfigured() {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(false));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertRemoteTransactionValid(tx);
}
@Test
public void addLocalTransaction_strictReplayProtectionOff_txWithoutChainId_chainIdIsConfigured() {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(false));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
}
@Test
public void addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsConfigured() {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionInvalid(tx, REPLAY_PROTECTED_SIGNATURE_REQUIRED);
}
@Test
public void
addRemoteTransactions_strictReplayProtectionOn_txWithoutChainId_chainIdIsConfigured() {
protocolSupportsTxReplayProtection(1337, true);
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertRemoteTransactionValid(tx);
}
@Test
public void
addLocalTransaction_strictReplayProtectionOn_txWithoutChainId_chainIdIsNotConfigured() {
protocolDoesNotSupportTxReplayProtection();
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertLocalTransactionValid(tx);
}
@Test
public void
addRemoteTransactions_strictReplayProtectionOn_txWithoutChainId_chainIdIsNotConfigured() {
protocolDoesNotSupportTxReplayProtection();
transactionPool = createTransactionPool(b -> b.strictTransactionReplayProtectionEnabled(true));
final Transaction tx = createTransactionWithoutChainId(1);
givenTransactionIsValid(tx);
assertRemoteTransactionValid(tx);
}
@Test
public void shouldIgnoreEIP1559TransactionWhenNotAllowed() {
final Transaction transaction =
createBaseTransaction(1)
.type(TransactionType.EIP1559)
.maxFeePerGas(Optional.of(Wei.of(100L)))
.maxPriorityFeePerGas(Optional.of(Wei.of(50L)))
.gasLimit(10)
.gasPrice(null)
.createTransaction(KEY_PAIR1);
givenTransactionIsValid(transaction);
assertLocalTransactionInvalid(transaction, INVALID_TRANSACTION_FORMAT);
}
@Test
public void shouldRejectGoQuorumTransactionWithNonZeroValue() {
when(transactionValidator.getGoQuorumCompatibilityMode()).thenReturn(true);
final Transaction transaction37 =
Transaction.builder().v(BigInteger.valueOf(37)).value(Wei.ONE).build();
final Transaction transaction38 =
Transaction.builder().v(BigInteger.valueOf(38)).value(Wei.ONE).build();
final ValidationResult<TransactionInvalidReason> result37 =
transactionPool.addLocalTransaction(transaction37);
final ValidationResult<TransactionInvalidReason> result38 =
transactionPool.addLocalTransaction(transaction38);
assertThat(result37.getInvalidReason())
.isEqualTo(TransactionInvalidReason.ETHER_VALUE_NOT_SUPPORTED);
assertThat(result38.getInvalidReason())
.isEqualTo(TransactionInvalidReason.ETHER_VALUE_NOT_SUPPORTED);
}
@Test
public void shouldAcceptZeroGasPriceFrontierTransactionsWhenMining() {
when(miningParameters.isMiningEnabled()).thenReturn(true);
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
}
@Test
public void shouldAcceptZeroGasPriceTransactionWhenMinGasPriceIsZero() {
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
}
private Transaction createTransactionWithoutChainId(final int transactionNumber) {
return createTransaction(transactionNumber, Optional.empty());
}
private void protocolDoesNotSupportTxReplayProtection() {
when(protocolSchedule.getChainId()).thenReturn(Optional.empty());
}
}

@ -0,0 +1,221 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.transactions;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.StubGenesisConfigOptions;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.BaseFeePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.plugin.data.TransactionType;
import org.hyperledger.besu.testutil.TestClock;
import java.math.BigInteger;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import org.junit.Ignore;
import org.junit.Test;
public class TransactionPoolLondonTest extends AbstractTransactionPoolTest {
private static final Wei BASE_FEE_FLOOR = Wei.of(7L);
@Override
protected AbstractPendingTransactionsSorter createPendingTransactionsSorter() {
return new BaseFeePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder()
.txPoolMaxSize(MAX_TRANSACTIONS)
.txPoolLimitByAccountPercentage(1)
.build(),
TestClock.system(ZoneId.systemDefault()),
metricsSystem,
protocolContext.getBlockchain()::getChainHeadHeader);
}
@Override
protected Transaction createTransaction(
final int transactionNumber, final Optional<BigInteger> maybeChainId) {
return createBaseTransaction(transactionNumber)
.chainId(maybeChainId)
.createTransaction(KEY_PAIR1);
}
@Override
protected Transaction createTransaction(final int transactionNumber, final Wei maxPrice) {
return createBaseTransaction(transactionNumber)
.maxFeePerGas(Optional.of(maxPrice))
.maxPriorityFeePerGas(Optional.of(maxPrice.divide(5L)))
.createTransaction(KEY_PAIR1);
}
@Override
protected TransactionTestFixture createBaseTransaction(final int transactionNumber) {
return new TransactionTestFixture()
.nonce(transactionNumber)
.gasLimit(blockGasLimit)
.gasPrice(null)
.maxFeePerGas(Optional.of(Wei.of(5000L)))
.maxPriorityFeePerGas(Optional.of(Wei.of(1000L)))
.type(TransactionType.EIP1559);
}
@Override
protected ExecutionContextTestFixture createExecutionContextTestFixture() {
final ProtocolSchedule protocolSchedule =
new ProtocolScheduleBuilder(
new StubGenesisConfigOptions().londonBlock(0L).baseFeePerGas(10L),
BigInteger.valueOf(1),
ProtocolSpecAdapters.create(0, Function.identity()),
new PrivacyParameters(),
false,
false,
EvmConfiguration.DEFAULT)
.createProtocolSchedule();
final ExecutionContextTestFixture executionContextTestFixture =
ExecutionContextTestFixture.builder().protocolSchedule(protocolSchedule).build();
final Block block =
new Block(
new BlockHeaderTestFixture()
.gasLimit(
executionContextTestFixture
.getBlockchain()
.getChainHeadBlock()
.getHeader()
.getGasLimit())
.difficulty(Difficulty.ONE)
.baseFeePerGas(Wei.of(10L))
.parentHash(executionContextTestFixture.getBlockchain().getChainHeadHash())
.number(executionContextTestFixture.getBlockchain().getChainHeadBlockNumber() + 1)
.buildHeader(),
new BlockBody(List.of(), List.of()));
executionContextTestFixture.getBlockchain().appendBlock(block, List.of());
return executionContextTestFixture;
}
@Override
protected FeeMarket getFeeMarket() {
return FeeMarket.london(0L, Optional.of(BASE_FEE_FLOOR));
}
@Override
protected Block appendBlock(
final Difficulty difficulty,
final BlockHeader parentBlock,
final Transaction... transactionsToAdd) {
final List<Transaction> transactionList = asList(transactionsToAdd);
final Block block =
new Block(
new BlockHeaderTestFixture()
.baseFeePerGas(Wei.of(10L))
.gasLimit(parentBlock.getGasLimit())
.difficulty(difficulty)
.parentHash(parentBlock.getHash())
.number(parentBlock.getNumber() + 1)
.buildHeader(),
new BlockBody(transactionList, emptyList()));
final List<TransactionReceipt> transactionReceipts =
transactionList.stream()
.map(transaction -> new TransactionReceipt(1, 1, emptyList(), Optional.empty()))
.collect(toList());
blockchain.appendBlock(block, transactionReceipts);
return block;
}
@Test
public void shouldAcceptZeroGasPriceFrontierTxsWhenMinGasPriceIsZeroAndLondonWithZeroBaseFee() {
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0, Optional.of(Wei.ZERO)));
whenBlockBaseFeeIsZero();
final Transaction frontierTransaction = createFrontierTransaction(0, Wei.ZERO);
givenTransactionIsValid(frontierTransaction);
assertLocalTransactionValid(frontierTransaction);
}
@Test
public void shouldAcceptZeroGasPrice1559TxsWhenMinGasPriceIsZeroAndLondonWithZeroBaseFee() {
when(miningParameters.getMinTransactionGasPrice()).thenReturn(Wei.ZERO);
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0, Optional.of(Wei.ZERO)));
whenBlockBaseFeeIsZero();
final Transaction transaction = createTransaction(0, Wei.ZERO);
givenTransactionIsValid(transaction);
assertLocalTransactionValid(transaction);
}
@Test
public void shouldAcceptBaseFeeFloorGasPriceFrontierTransactionsWhenMining() {
final Transaction frontierTransaction = createFrontierTransaction(0, BASE_FEE_FLOOR);
givenTransactionIsValid(frontierTransaction);
assertLocalTransactionValid(frontierTransaction);
}
@Test
@Override
@Ignore
public void shouldRejectLocalTransactionIfFeeCapExceeded() {
// ignore since this is going to fail until the branch with the fix is released
}
private void whenBlockBaseFeeIsZero() {
final BlockHeader header =
BlockHeaderBuilder.fromHeader(blockchain.getChainHeadHeader())
.baseFee(Wei.ZERO)
.blockHeaderFunctions(new MainnetBlockHeaderFunctions())
.parentHash(blockchain.getChainHeadHash())
.buildBlockHeader();
blockchain.appendBlock(new Block(header, BlockBody.empty()), emptyList());
}
private Transaction createFrontierTransaction(final int transactionNumber, final Wei gasPrice) {
return new TransactionTestFixture()
.nonce(transactionNumber)
.gasPrice(gasPrice)
.gasLimit(blockGasLimit)
.type(TransactionType.FRONTIER)
.createTransaction(KEY_PAIR1);
}
}
Loading…
Cancel
Save