diff --git a/CHANGELOG.md b/CHANGELOG.md index 3173afe3bb7..250dd2c66f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index 2c4382234dc..5e4f5caa552 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -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; @@ -132,6 +133,7 @@ private List createTransactionSelectors( new BlockSizeTransactionSelector(context), new PriceTransactionSelector(context), new BlobPriceTransactionSelector(context), + new MinPriorityFeePerGasTransactionSelector(context), new ProcessingResultTransactionSelector(context)); } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java new file mode 100644 index 00000000000..91085f4f020 --- /dev/null +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/MinPriorityFeePerGasTransactionSelector.java @@ -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; + } +} diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index 63f9476dcb3..39a4f0673e8 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -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, diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java index 57b89f8a1d7..1ef0fa8f2ff 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java @@ -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)); + } } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java new file mode 100644 index 00000000000..a86dc73ee46 --- /dev/null +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/MinPriorityFeePerGasTransactionSelectorTest.java @@ -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; + } +} diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 75b433f4a63..beacad8710f 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -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') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java index ffae842ca72..0cdaea997e2 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java @@ -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 maybeInvalidReason;