Skip to content

Commit

Permalink
Add transaction selector based on min priority fee parameter (#6083)
Browse files Browse the repository at this point in the history
Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
  • Loading branch information
Gabriel-Trintinalia committed Nov 2, 2023
1 parent 236779d commit 41b9575
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
- Accept `input` and `data` field for the payload of transaction-related RPC methods [#6094](https://github.com/hyperledger/besu/pull/6094)
- Add APIs to set and get the min gas price a transaction must pay for being selected during block creation [#6097](https://github.com/hyperledger/besu/pull/6097)
- TraceService: return results for transactions in block [#6086](https://github.com/hyperledger/besu/pull/6086)
- New option `--min-priority-fee` that sets the minimum priority fee a transaction must meet to be selected for a block. [#6080](https://github.com/hyperledger/besu/pull/6080) [#6083](https://github.com/hyperledger/besu/pull/6083)
- Implement new `miner_setMinPriorityFee` and `miner_getMinPriorityFee` RPC methods [#6080](https://github.com/hyperledger/besu/pull/6080)

### Bug fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.AllAcceptingTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.BlobPriceTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.BlockSizeTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.MinPriorityFeePerGasTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.PriceTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.ProcessingResultTransactionSelector;
import org.hyperledger.besu.ethereum.chain.Blockchain;
Expand Down Expand Up @@ -132,6 +133,7 @@ private List<AbstractTransactionSelector> createTransactionSelectors(
new BlockSizeTransactionSelector(context),
new PriceTransactionSelector(context),
new BlobPriceTransactionSelector(context),
new MinPriorityFeePerGasTransactionSelector(context),
new ProcessingResultTransactionSelector(context));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.blockcreation.txselection.selectors;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;

/** This class is responsible for selecting transactions based on the minimum priority fee. */
public class MinPriorityFeePerGasTransactionSelector extends AbstractTransactionSelector {

/**
* Constructor for MinPriorityFeeSelector.
*
* @param context The context of block selection.
*/
public MinPriorityFeePerGasTransactionSelector(final BlockSelectionContext context) {
super(context);
}

/**
* Evaluates a transaction before processing.
*
* @param pendingTransaction The transaction to be evaluated.
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @return TransactionSelectionResult. If the priority fee is below the minimum, it returns an
* invalid transient result. Otherwise, it returns a selected result.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final PendingTransaction pendingTransaction,
final TransactionSelectionResults transactionSelectionResults) {
if (isPriorityFeePriceBelowMinimum(pendingTransaction)) {
return TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN;
}
return TransactionSelectionResult.SELECTED;
}

/**
* Checks if the priority fee price is below the minimum.
*
* @param pendingTransaction The transaction to check.
* @return boolean. Returns true if the minimum priority fee price is below the minimum, false
* otherwise.
*/
private boolean isPriorityFeePriceBelowMinimum(final PendingTransaction pendingTransaction) {
// Priority txs are exempt from this check
if (pendingTransaction.hasPriority()) {
return false;
}
Wei priorityFeePerGas =
pendingTransaction
.getTransaction()
.getEffectivePriorityFeePerGas(context.processableBlockHeader().getBaseFee());
return priorityFeePerGas.lessThan(context.miningParameters().getMinPriorityFeePerGas());
}

/**
* No evaluation is performed post-processing.
*
* @param pendingTransaction The processed transaction.
* @param processingResult The result of the transaction processing.
* @return Always returns SELECTED.
*/
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final PendingTransaction pendingTransaction,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
return TransactionSelectionResult.SELECTED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,35 @@ public void decreaseOfMinGasPriceAtRuntimeIncludeTxThatWasPreviouslyNotSelected(
assertThat(results2.getNotSelectedTransactions()).isEmpty();
}

@Test
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() {
ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE);
miningParameters.setMinPriorityFeePerGas(Wei.of(7));
final Transaction txSelected = createTransaction(1, Wei.of(8), 100_000);
ensureTransactionIsValid(txSelected);
// transaction txNotSelected should not be selected
final Transaction txNotSelected = createTransaction(2, Wei.of(7), 100_000);
ensureTransactionIsValid(txNotSelected);
transactionPool.addRemoteTransactions(List.of(txSelected, txNotSelected));

final BlockTransactionSelector selector =
createBlockSelector(
transactionProcessor,
blockHeader,
Wei.ZERO,
AddressHelpers.ofValue(1),
Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT);

final TransactionSelectionResults results = selector.buildTransactionListForBlock();

assertThat(results.getSelectedTransactions()).containsOnly(txSelected);
assertThat(results.getNotSelectedTransactions())
.containsOnly(
entry(
txNotSelected, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN));
}

protected BlockTransactionSelector createBlockSelector(
final MainnetTransactionProcessor transactionProcessor,
final ProcessableBlockHeader blockHeader,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,50 @@ public void transactionFromSameSenderWithMixedTypes() {
.containsExactly(txFrontier1, txLondon1, txFrontier2, txLondon2);
assertThat(results.getNotSelectedTransactions()).isEmpty();
}

@Test
@Override
public void shouldNotSelectTransactionsWithPriorityFeeLessThanConfig() {
ProcessableBlockHeader blockHeader = createBlock(5_000_000, Wei.ONE);
miningParameters.setMinPriorityFeePerGas(Wei.of(7));

final Transaction txSelected1 = createEIP1559Transaction(1, Wei.of(8), Wei.of(8), 100_000);
ensureTransactionIsValid(txSelected1);

// transaction txNotSelected1 should not be selected
final Transaction txNotSelected1 = createEIP1559Transaction(2, Wei.of(7), Wei.of(7), 100_000);
ensureTransactionIsValid(txNotSelected1);

// transaction txSelected2 should be selected
final Transaction txSelected2 = createEIP1559Transaction(3, Wei.of(8), Wei.of(8), 100_000);
ensureTransactionIsValid(txSelected2);

// transaction txNotSelected2 should not be selected
final Transaction txNotSelected2 = createEIP1559Transaction(4, Wei.of(8), Wei.of(6), 100_000);
ensureTransactionIsValid(txNotSelected2);

transactionPool.addRemoteTransactions(
List.of(txSelected1, txNotSelected1, txSelected2, txNotSelected2));

assertThat(transactionPool.getPendingTransactions().size()).isEqualTo(4);

final BlockTransactionSelector selector =
createBlockSelector(
transactionProcessor,
blockHeader,
Wei.ZERO,
AddressHelpers.ofValue(1),
Wei.ZERO,
MIN_OCCUPANCY_100_PERCENT);

final TransactionSelectionResults results = selector.buildTransactionListForBlock();

assertThat(results.getSelectedTransactions()).containsOnly(txSelected1, txSelected2);
assertThat(results.getNotSelectedTransactions())
.containsOnly(
entry(
txNotSelected1, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN),
entry(
txNotSelected2, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.blockcreation;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.AbstractTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.MinPriorityFeePerGasTransactionSelector;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class MinPriorityFeePerGasTransactionSelectorTest {
private AbstractTransactionSelector transactionSelector;

private final int minPriorityFeeParameter = 7;

@BeforeEach
public void initialize() {
MiningParameters miningParameters =
MiningParameters.newDefault().setMinPriorityFeePerGas(Wei.of(minPriorityFeeParameter));
BlockSelectionContext context =
new BlockSelectionContext(
miningParameters,
null,
null,
mock(ProcessableBlockHeader.class),
null,
null,
null,
null);
transactionSelector = new MinPriorityFeePerGasTransactionSelector(context);
}

@Test
public void shouldNotSelectWhen_PriorityFeePerGas_IsLessThan_MinPriorityFeePerGas() {
var transaction = mockTransactionWithPriorityFee(minPriorityFeeParameter - 1);
assertSelectionResult(
transaction, TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN);
}

@Test
public void shouldSelectWhen_PriorityFeePerGas_IsEqual_MinPriorityFeePerGas() {
var transaction = mockTransactionWithPriorityFee(minPriorityFeeParameter);
assertSelectionResult(transaction, TransactionSelectionResult.SELECTED);
}

@Test
public void shouldSelectWhen_PriorityFeePerGas_IsGreaterThan_MinPriorityFeePerGas() {
var transaction = mockTransactionWithPriorityFee(minPriorityFeeParameter + 1);
assertSelectionResult(transaction, TransactionSelectionResult.SELECTED);
}

@Test
public void shouldSelectWhenPrioritySender() {
var prioritySenderTransaction = mockTransactionWithPriorityFee(minPriorityFeeParameter - 1);
assertSelectionResult(
prioritySenderTransaction,
TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN);
when(prioritySenderTransaction.hasPriority()).thenReturn(true);
assertSelectionResult(prioritySenderTransaction, TransactionSelectionResult.SELECTED);
}

private void assertSelectionResult(
final PendingTransaction transaction, final TransactionSelectionResult expectedResult) {
var actualResult = transactionSelector.evaluateTransactionPreProcessing(transaction, null);
assertThat(actualResult).isEqualTo(expectedResult);
}

private PendingTransaction mockTransactionWithPriorityFee(final int priorityFeePerGas) {
PendingTransaction mockTransaction = mock(PendingTransaction.class);
Transaction transaction = mock(Transaction.class);
when(mockTransaction.getTransaction()).thenReturn(transaction);
when(transaction.getEffectivePriorityFeePerGas(any())).thenReturn(Wei.of(priorityFeePerGas));
return mockTransaction;
}
}
2 changes: 1 addition & 1 deletion plugin-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,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 = 'ZXBvp7wuHQ8j4Gty2zg/gKdzgrOXSpehYukMuH98W/Y='
knownHash = 'kyCYfllc1IcisRZIYuLxhC+0+POCzcMQPhE8F8mx1Ns='
}
check.dependsOn('checkAPIChanges')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,19 @@ public String toString() {
public static final TransactionSelectionResult CURRENT_TX_PRICE_BELOW_MIN =
TransactionSelectionResult.invalidTransient("CURRENT_TX_PRICE_BELOW_MIN");
/**
* The transaction has not been selected since its data price is below the current network data
* The transaction has not been selected since its blob price is below the current network blob
* price, but the selection should continue.
*/
public static final TransactionSelectionResult BLOB_PRICE_BELOW_CURRENT_MIN =
TransactionSelectionResult.invalidTransient("BLOB_PRICE_BELOW_CURRENT_MIN");

/**
* The transaction has not been selected since its priority fee is below the configured min
* priority fee per gas, but the selection should continue.
*/
public static final TransactionSelectionResult PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN =
TransactionSelectionResult.invalidTransient("PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN");

private final Status status;
private final Optional<String> maybeInvalidReason;

Expand Down

0 comments on commit 41b9575

Please sign in to comment.