mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-07 21:13:56 -05:00
Support pending transaction score when saving and restoring txpool (#8363)
Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
- Add TLS/mTLS options and configure the GraphQL HTTP service[#7910](https://github.com/hyperledger/besu/pull/7910)
|
||||
- Update `eth_getLogs` to return a `Block not found` error when the requested block is not found. [#8290](https://github.com/hyperledger/besu/pull/8290)
|
||||
- Change `Invalid block, unable to parse RLP` RPC error message to `Invalid block param (block not found)` [#8328](https://github.com/hyperledger/besu/pull/8328)
|
||||
- Support pending transaction score when saving and restoring txpool [#8363](https://github.com/hyperledger/besu/pull/8363)
|
||||
|
||||
### Bug fixes
|
||||
- Add missing RPC method `debug_accountRange` to `RpcMethod.java` so this method can be used with `--rpc-http-api-method-no-auth` [#8153](https://github.com/hyperledger/besu/issues/8153)
|
||||
|
||||
@@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -1080,7 +1081,8 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
|
||||
.nonce(transactionNumber)
|
||||
.createTransaction(KEYS1),
|
||||
true,
|
||||
true);
|
||||
true,
|
||||
MAX_SCORE);
|
||||
}
|
||||
|
||||
private static BlockHeader mockBlockHeader() {
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOBS_FULL;
|
||||
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
|
||||
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_BLOB_GAS;
|
||||
@@ -172,12 +173,12 @@ class BlobSizeTransactionSelectorTest {
|
||||
|
||||
private PendingTransaction createEIP1559PendingTransaction() {
|
||||
return PendingTransaction.newPendingTransaction(
|
||||
createTransaction(TransactionType.EIP1559, 0), false, false);
|
||||
createTransaction(TransactionType.EIP1559, 0), false, false, MAX_SCORE);
|
||||
}
|
||||
|
||||
private PendingTransaction createBlobPendingTransaction(final int blobCount) {
|
||||
return PendingTransaction.newPendingTransaction(
|
||||
createTransaction(TransactionType.BLOB, blobCount), false, false);
|
||||
createTransaction(TransactionType.BLOB, blobCount), false, false, MAX_SCORE);
|
||||
}
|
||||
|
||||
private Transaction createTransaction(final TransactionType type, final int blobCount) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_FULL;
|
||||
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD;
|
||||
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
|
||||
@@ -247,7 +248,7 @@ class BlockSizeTransactionSelectorTest {
|
||||
|
||||
private PendingTransaction createPendingTransaction(final long gasLimit) {
|
||||
return PendingTransaction.newPendingTransaction(
|
||||
createTransaction(TransactionType.EIP1559, gasLimit), false, false);
|
||||
createTransaction(TransactionType.EIP1559, gasLimit), false, false, MAX_SCORE);
|
||||
}
|
||||
|
||||
private Transaction createTransaction(final TransactionType type, final long gasLimit) {
|
||||
|
||||
@@ -48,7 +48,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*/
|
||||
public abstract class PendingTransaction
|
||||
implements org.hyperledger.besu.datatypes.PendingTransaction {
|
||||
static final int NOT_INITIALIZED = -1;
|
||||
public static final Byte MAX_SCORE = Byte.MAX_VALUE;
|
||||
private static final int NOT_INITIALIZED = -1;
|
||||
private static final AtomicLong TRANSACTIONS_ADDED = new AtomicLong();
|
||||
private final Transaction transaction;
|
||||
private final long addedAt;
|
||||
@@ -58,37 +59,42 @@ public abstract class PendingTransaction
|
||||
private int memorySize = NOT_INITIALIZED;
|
||||
|
||||
private PendingTransaction(
|
||||
final Transaction transaction, final long addedAt, final long sequence, final byte score) {
|
||||
final Transaction transaction, final byte score, final long addedAt, final long sequence) {
|
||||
this.transaction = transaction;
|
||||
this.addedAt = addedAt;
|
||||
this.sequence = sequence;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
private PendingTransaction(final Transaction transaction, final long addedAt) {
|
||||
this(transaction, addedAt, TRANSACTIONS_ADDED.getAndIncrement(), Byte.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static PendingTransaction newPendingTransaction(
|
||||
final Transaction transaction, final boolean isLocal, final boolean hasPriority) {
|
||||
return newPendingTransaction(transaction, isLocal, hasPriority, System.currentTimeMillis());
|
||||
private PendingTransaction(final Transaction transaction, final byte score, final long addedAt) {
|
||||
this(transaction, score, addedAt, TRANSACTIONS_ADDED.getAndIncrement());
|
||||
}
|
||||
|
||||
public static PendingTransaction newPendingTransaction(
|
||||
final Transaction transaction,
|
||||
final boolean isLocal,
|
||||
final boolean hasPriority,
|
||||
final byte score) {
|
||||
return newPendingTransaction(
|
||||
transaction, isLocal, hasPriority, score, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static PendingTransaction newPendingTransaction(
|
||||
final Transaction transaction,
|
||||
final boolean isLocal,
|
||||
final boolean hasPriority,
|
||||
final byte score,
|
||||
final long addedAt) {
|
||||
if (isLocal) {
|
||||
if (hasPriority) {
|
||||
return new Local.Priority(transaction, addedAt);
|
||||
return new Local.Priority(transaction, score, addedAt);
|
||||
}
|
||||
return new Local(transaction, addedAt);
|
||||
return new Local(transaction, score, addedAt);
|
||||
}
|
||||
if (hasPriority) {
|
||||
return new Remote.Priority(transaction, addedAt);
|
||||
return new Remote.Priority(transaction, score, addedAt);
|
||||
}
|
||||
return new Remote(transaction, addedAt);
|
||||
return new Remote(transaction, score, addedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -311,16 +317,16 @@ public abstract class PendingTransaction
|
||||
|
||||
public static class Local extends PendingTransaction {
|
||||
|
||||
public Local(final Transaction transaction, final long addedAt) {
|
||||
super(transaction, addedAt);
|
||||
public Local(final Transaction transaction, final byte score, final long addedAt) {
|
||||
super(transaction, score, addedAt);
|
||||
}
|
||||
|
||||
public Local(final Transaction transaction) {
|
||||
this(transaction, System.currentTimeMillis());
|
||||
this(transaction, MAX_SCORE, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private Local(final long sequence, final byte score, final Transaction transaction) {
|
||||
super(transaction, System.currentTimeMillis(), sequence, score);
|
||||
super(transaction, score, System.currentTimeMillis(), sequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -340,11 +346,11 @@ public abstract class PendingTransaction
|
||||
|
||||
public static class Priority extends Local {
|
||||
public Priority(final Transaction transaction) {
|
||||
this(transaction, System.currentTimeMillis());
|
||||
this(transaction, MAX_SCORE, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Priority(final Transaction transaction, final long addedAt) {
|
||||
super(transaction, addedAt);
|
||||
public Priority(final Transaction transaction, final byte score, final long addedAt) {
|
||||
super(transaction, score, addedAt);
|
||||
}
|
||||
|
||||
public Priority(final long sequence, final byte score, final Transaction transaction) {
|
||||
@@ -365,16 +371,16 @@ public abstract class PendingTransaction
|
||||
|
||||
public static class Remote extends PendingTransaction {
|
||||
|
||||
public Remote(final Transaction transaction, final long addedAt) {
|
||||
super(transaction, addedAt);
|
||||
public Remote(final Transaction transaction, final byte score, final long addedAt) {
|
||||
super(transaction, score, addedAt);
|
||||
}
|
||||
|
||||
public Remote(final Transaction transaction) {
|
||||
this(transaction, System.currentTimeMillis());
|
||||
this(transaction, MAX_SCORE, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private Remote(final long sequence, final byte score, final Transaction transaction) {
|
||||
super(transaction, System.currentTimeMillis(), sequence, score);
|
||||
super(transaction, score, System.currentTimeMillis(), sequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -394,11 +400,11 @@ public abstract class PendingTransaction
|
||||
|
||||
public static class Priority extends Remote {
|
||||
public Priority(final Transaction transaction) {
|
||||
this(transaction, System.currentTimeMillis());
|
||||
this(transaction, MAX_SCORE, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Priority(final Transaction transaction, final long addedAt) {
|
||||
super(transaction, addedAt);
|
||||
public Priority(final Transaction transaction, final byte score, final long addedAt) {
|
||||
super(transaction, score, addedAt);
|
||||
}
|
||||
|
||||
public Priority(final long sequence, final byte score, final Transaction transaction) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.eth.transactions;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.CHAIN_HEAD_NOT_AVAILABLE;
|
||||
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE;
|
||||
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INTERNAL_ERROR;
|
||||
@@ -32,6 +33,8 @@ import org.hyperledger.besu.ethereum.chain.BlockAddedObserver;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
@@ -190,7 +193,7 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
public ValidationResult<TransactionInvalidReason> addTransactionViaApi(
|
||||
final Transaction transaction) {
|
||||
|
||||
final var result = addTransaction(transaction, true);
|
||||
final var result = addTransaction(transaction, true, MAX_SCORE);
|
||||
if (result.isValid()) {
|
||||
localSenders.add(transaction.getSender());
|
||||
transactionBroadcaster.onTransactionsAdded(List.of(transaction));
|
||||
@@ -211,7 +214,7 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
Collectors.toMap(
|
||||
Transaction::getHash,
|
||||
transaction -> {
|
||||
final var result = addTransaction(transaction, false);
|
||||
final var result = addTransaction(transaction, false, MAX_SCORE);
|
||||
if (result.isValid()) {
|
||||
addedTransactions.add(transaction);
|
||||
}
|
||||
@@ -241,7 +244,7 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
}
|
||||
|
||||
private ValidationResult<TransactionInvalidReason> addTransaction(
|
||||
final Transaction transaction, final boolean isLocal) {
|
||||
final Transaction transaction, final boolean isLocal, final byte score) {
|
||||
|
||||
final boolean hasPriority = isPriorityTransaction(transaction, isLocal);
|
||||
|
||||
@@ -261,7 +264,7 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
if (validationResult.result.isValid()) {
|
||||
final TransactionAddedResult status =
|
||||
pendingTransactions.addTransaction(
|
||||
PendingTransaction.newPendingTransaction(transaction, isLocal, hasPriority),
|
||||
PendingTransaction.newPendingTransaction(transaction, isLocal, hasPriority, score),
|
||||
validationResult.maybeAccount);
|
||||
if (status.isSuccess()) {
|
||||
LOG.atTrace()
|
||||
@@ -822,8 +825,10 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
.map(
|
||||
ptx -> {
|
||||
final BytesValueRLPOutput rlp = new BytesValueRLPOutput();
|
||||
ptx.getTransaction().writeTo(rlp);
|
||||
return (ptx.isReceivedFromLocalSource() ? "l" : "r")
|
||||
TransactionEncoder.encodeRLP(
|
||||
ptx.getTransaction(), rlp, EncodingContext.POOLED_TRANSACTION);
|
||||
return ptx.getScore()
|
||||
+ (ptx.isReceivedFromLocalSource() ? "l" : "r")
|
||||
+ rlp.encoded().toBase64String();
|
||||
})
|
||||
.mapToInt(
|
||||
@@ -870,12 +875,16 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
.takeWhile(unused -> !isCancelled.get())
|
||||
.map(
|
||||
line -> {
|
||||
final boolean isLocal = line.charAt(0) == 'l';
|
||||
final var scoreStr = parseScore(line);
|
||||
final byte score =
|
||||
scoreStr.isEmpty() ? MAX_SCORE : Byte.parseByte(scoreStr);
|
||||
final boolean isLocal = line.charAt(scoreStr.length()) == 'l';
|
||||
final Transaction tx =
|
||||
Transaction.readFrom(Bytes.fromBase64String(line.substring(1)));
|
||||
Transaction.readFrom(
|
||||
Bytes.fromBase64String(line.substring(scoreStr.length() + 1)));
|
||||
|
||||
final ValidationResult<TransactionInvalidReason> result =
|
||||
addTransaction(tx, isLocal);
|
||||
addTransaction(tx, isLocal, score);
|
||||
return result.isValid() ? "OK" : result.getInvalidReason().name();
|
||||
})
|
||||
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
|
||||
@@ -909,6 +918,15 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
}
|
||||
}
|
||||
|
||||
private String parseScore(final String line) {
|
||||
int i = 0;
|
||||
final var sbScore = new StringBuilder();
|
||||
while ("1234567890-".indexOf(line.charAt(i)) >= 0) {
|
||||
sbScore.append(line.charAt(i++));
|
||||
}
|
||||
return sbScore.toString();
|
||||
}
|
||||
|
||||
private void removeProcessedLines(final File saveFile, final long processedLines)
|
||||
throws IOException {
|
||||
|
||||
|
||||
@@ -386,6 +386,18 @@ public abstract class AbstractTransactionPoolTestBase {
|
||||
.thenReturn(valid());
|
||||
}
|
||||
|
||||
protected void givenAllTransactionsAreValid() {
|
||||
when(transactionValidatorFactory
|
||||
.get()
|
||||
.validate(any(), any(Optional.class), any(Optional.class), any()))
|
||||
.thenReturn(valid());
|
||||
when(transactionValidatorFactory
|
||||
.get()
|
||||
.validateForSender(
|
||||
any(), nullable(Account.class), any(TransactionValidationParams.class)))
|
||||
.thenReturn(valid());
|
||||
}
|
||||
|
||||
protected abstract Block appendBlock(
|
||||
final Difficulty difficulty,
|
||||
final BlockHeader parentBlock,
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.eth.transactions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
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.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.layered.BaseFeePrioritizedTransactions;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.layered.EndLayer;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredPendingTransactions;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.layered.ReadyTransactions;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.layered.SparseTransactions;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.layered.TransactionsLayer;
|
||||
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class TransactionPoolSaveRestoreTest extends AbstractTransactionPoolTestBase {
|
||||
@TempDir static Path tempDir;
|
||||
Path saveFilePath;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
saveFilePath = tempDir.resolve("txpool.dump");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
saveFilePath.toFile().delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PendingTransactions createPendingTransactions(
|
||||
final TransactionPoolConfiguration poolConfig,
|
||||
final BiFunction<PendingTransaction, PendingTransaction, Boolean>
|
||||
transactionReplacementTester) {
|
||||
|
||||
final var txPoolMetrics = new TransactionPoolMetrics(metricsSystem);
|
||||
final TransactionsLayer sparseLayer =
|
||||
new SparseTransactions(
|
||||
poolConfig,
|
||||
ethScheduler,
|
||||
new EndLayer(txPoolMetrics),
|
||||
txPoolMetrics,
|
||||
transactionReplacementTester,
|
||||
new BlobCache());
|
||||
final TransactionsLayer readyLayer =
|
||||
new ReadyTransactions(
|
||||
poolConfig,
|
||||
ethScheduler,
|
||||
sparseLayer,
|
||||
txPoolMetrics,
|
||||
transactionReplacementTester,
|
||||
new BlobCache());
|
||||
return new LayeredPendingTransactions(
|
||||
poolConfig,
|
||||
new BaseFeePrioritizedTransactions(
|
||||
poolConfig,
|
||||
protocolContext.getBlockchain()::getChainHeadHeader,
|
||||
ethScheduler,
|
||||
readyLayer,
|
||||
txPoolMetrics,
|
||||
transactionReplacementTester,
|
||||
FeeMarket.london(0L),
|
||||
new BlobCache(),
|
||||
MiningConfiguration.newDefault()),
|
||||
ethScheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionContextTestFixture createExecutionContextTestFixture() {
|
||||
return createExecutionContextTestFixtureBaseFeeMarket();
|
||||
}
|
||||
|
||||
@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) {
|
||||
return appendBlockBaseFeeMarket(difficulty, parentBlock, transactionsToAdd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransactionTestFixture createBaseTransaction(final int nonce) {
|
||||
return createBaseTransactionBaseFeeMarket(nonce);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transaction createTransaction(final int nonce, final Wei maxPrice) {
|
||||
return createTransactionBaseFeeMarket(nonce, maxPrice);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
public void localTransactionIsSavedAndRestored(final boolean noLocalPriority)
|
||||
throws ExecutionException, InterruptedException, TimeoutException, IOException {
|
||||
// create a txpool with save and restore enabled
|
||||
this.transactionPool =
|
||||
createTransactionPool(
|
||||
b ->
|
||||
b.noLocalPriority(noLocalPriority)
|
||||
.enableSaveRestore(true)
|
||||
.saveFile(saveFilePath.toFile()));
|
||||
|
||||
final Transaction transaction = createTransaction(noLocalPriority ? 0 : 1);
|
||||
|
||||
givenTransactionIsValid(transaction);
|
||||
|
||||
addAndAssertTransactionViaApiValid(transaction, noLocalPriority);
|
||||
|
||||
// disabling the txpool, forces a save to file
|
||||
transactionPool.setDisabled().get(10, TimeUnit.SECONDS);
|
||||
|
||||
// after being disabled the txpool must be empty
|
||||
assertThat(transactionPool.getPendingTransactions()).isEmpty();
|
||||
|
||||
final var savedContent = Files.readString(saveFilePath, StandardCharsets.US_ASCII);
|
||||
|
||||
assertThat(savedContent).isEqualToIgnoringNewLines("127l" + transaction2Base64(transaction));
|
||||
|
||||
// re-enabling the txpool restores from file
|
||||
transactionPool.setEnabled().get(10, TimeUnit.SECONDS);
|
||||
|
||||
assertThat(transactionPool.getPendingTransactions()).size().isEqualTo(1);
|
||||
|
||||
final var restoredPendingTx = transactionPool.getPendingTransactions().iterator().next();
|
||||
|
||||
assertThat(restoredPendingTx.getTransaction()).isEqualTo(transaction);
|
||||
assertThat(restoredPendingTx.isReceivedFromLocalSource()).isTrue();
|
||||
assertThat(restoredPendingTx.hasPriority()).isNotEqualTo(noLocalPriority);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteTransactionIsSavedAndRestored()
|
||||
throws ExecutionException, InterruptedException, TimeoutException, IOException {
|
||||
// create a txpool with save and restore enabled
|
||||
this.transactionPool =
|
||||
createTransactionPool(b -> b.enableSaveRestore(true).saveFile(saveFilePath.toFile()));
|
||||
|
||||
final Transaction transaction = createTransaction(2);
|
||||
|
||||
givenTransactionIsValid(transaction);
|
||||
|
||||
addAndAssertRemoteTransactionsValid(transaction);
|
||||
|
||||
// disabling the txpool, forces a save to file
|
||||
transactionPool.setDisabled().get(10, TimeUnit.SECONDS);
|
||||
|
||||
// after being disabled the txpool must be empty
|
||||
assertThat(transactionPool.getPendingTransactions()).isEmpty();
|
||||
|
||||
final var savedContent = Files.readString(saveFilePath, StandardCharsets.US_ASCII);
|
||||
|
||||
assertThat(savedContent).isEqualToIgnoringNewLines("127r" + transaction2Base64(transaction));
|
||||
|
||||
// re-enabling the txpool restores from file
|
||||
transactionPool.setEnabled().get(10, TimeUnit.SECONDS);
|
||||
|
||||
assertThat(transactionPool.getPendingTransactions()).size().isEqualTo(1);
|
||||
|
||||
final var restoredPendingTx = transactionPool.getPendingTransactions().iterator().next();
|
||||
|
||||
assertThat(restoredPendingTx.getTransaction()).isEqualTo(transaction);
|
||||
assertThat(restoredPendingTx.isReceivedFromLocalSource()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dumpFileWithoutScoreIsRestored() throws IOException {
|
||||
|
||||
// create a save file with one local and one remote tx, both without score
|
||||
final var noScoreContent =
|
||||
"""
|
||||
luFoC+FcBgIID6IITiIcf////////gASAwAGga1337/7O7cp7jaMTu9X230+6mLJciebaO5nrsgDRp1CgA5MCvzfmS4H3NqF0DIxJGl8atRTkKmFwLMZgPpkVTqQ=
|
||||
ruFoC+FcBgIID6IITiIcf////////gASAwAGglm0VMcNQmOS0aE5CJP1Lm7eBbFQIRvmwgUcfEka9sVagYWy/2d2tJHojo2smAIJgwLbud9Dr+f1lbxo1dSOBfmE=
|
||||
""";
|
||||
|
||||
Files.writeString(saveFilePath, noScoreContent);
|
||||
|
||||
givenAllTransactionsAreValid();
|
||||
|
||||
// create a txpool with save and restore enabled
|
||||
this.transactionPool =
|
||||
createTransactionPool(b -> b.enableSaveRestore(true).saveFile(saveFilePath.toFile()));
|
||||
|
||||
await().until(() -> transactionPool.getPendingTransactions().size() == 2);
|
||||
|
||||
assertThat(transactionPool.getPendingTransactions())
|
||||
.map(PendingTransaction::getScore)
|
||||
.allMatch(score -> score == Byte.MAX_VALUE);
|
||||
assertThat(transactionPool.getPendingTransactions())
|
||||
.map(PendingTransaction::isReceivedFromLocalSource)
|
||||
.filteredOn(Boolean::booleanValue)
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dumpFileWithLowestScoreTxIsRestored() throws IOException {
|
||||
|
||||
// create a save file with one remote tx with the lowest score
|
||||
final var noScoreContent =
|
||||
"""
|
||||
-128ruFoC+FcBgIID6IITiIcf////////gASAwAGga1337/7O7cp7jaMTu9X230+6mLJciebaO5nrsgDRp1CgA5MCvzfmS4H3NqF0DIxJGl8atRTkKmFwLMZgPpkVTqQ=
|
||||
""";
|
||||
|
||||
Files.writeString(saveFilePath, noScoreContent);
|
||||
|
||||
givenAllTransactionsAreValid();
|
||||
|
||||
// create a txpool with save and restore enabled
|
||||
this.transactionPool =
|
||||
createTransactionPool(b -> b.enableSaveRestore(true).saveFile(saveFilePath.toFile()));
|
||||
|
||||
await().until(() -> transactionPool.getPendingTransactions().size() == 1);
|
||||
|
||||
assertThat(transactionPool.getPendingTransactions())
|
||||
.map(PendingTransaction::getScore)
|
||||
.allMatch(score -> score == Byte.MIN_VALUE);
|
||||
assertThat(transactionPool.getPendingTransactions())
|
||||
.map(PendingTransaction::isReceivedFromLocalSource)
|
||||
.first()
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
private String transaction2Base64(final Transaction transaction) {
|
||||
final BytesValueRLPOutput rlp = new BytesValueRLPOutput();
|
||||
TransactionEncoder.encodeRLP(transaction, rlp, EncodingContext.POOLED_TRANSACTION);
|
||||
return rlp.encoded().toBase64String();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.transactions.layered;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.core.TransactionTestFixture.createSignedCodeDelegation;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
|
||||
import org.hyperledger.besu.crypto.KeyPair;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
@@ -253,7 +254,7 @@ public class BaseTransactionPoolTest {
|
||||
|
||||
protected PendingTransaction createRemotePendingTransaction(
|
||||
final Transaction transaction, final boolean hasPriority) {
|
||||
return PendingTransaction.newPendingTransaction(transaction, false, hasPriority);
|
||||
return PendingTransaction.newPendingTransaction(transaction, false, hasPriority, MAX_SCORE);
|
||||
}
|
||||
|
||||
protected PendingTransaction createLocalPendingTransaction(final Transaction transaction) {
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.transactions.layered;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.layered.LayeredRemovalReason.PoolRemovalReason.INVALIDATED;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -307,7 +308,7 @@ public class ReplayTest {
|
||||
}
|
||||
assertThat(
|
||||
pendingTransactions.addTransaction(
|
||||
PendingTransaction.newPendingTransaction(tx, false, false),
|
||||
PendingTransaction.newPendingTransaction(tx, false, false, MAX_SCORE),
|
||||
Optional.of(mockAccount)))
|
||||
.isNotEqualTo(TransactionAddedResult.INTERNAL_ERROR);
|
||||
if (tx.getSender().equals(senderToLog)) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.eth.transactions.sorter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MAX_SCORE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ADDED;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ALREADY_KNOWN;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.REJECTED_UNDERPRICED_REPLACEMENT;
|
||||
@@ -762,15 +763,15 @@ public abstract class AbstractPendingTransactionsTestBase {
|
||||
|
||||
private PendingTransaction createRemotePendingTransaction(
|
||||
final Transaction transaction, final long addedAt) {
|
||||
return PendingTransaction.newPendingTransaction(transaction, false, false, addedAt);
|
||||
return PendingTransaction.newPendingTransaction(transaction, false, false, MAX_SCORE, addedAt);
|
||||
}
|
||||
|
||||
private PendingTransaction createRemotePendingTransaction(final Transaction transaction) {
|
||||
return PendingTransaction.newPendingTransaction(transaction, false, false);
|
||||
return PendingTransaction.newPendingTransaction(transaction, false, false, MAX_SCORE);
|
||||
}
|
||||
|
||||
private PendingTransaction createLocalPendingTransaction(final Transaction transaction) {
|
||||
return PendingTransaction.newPendingTransaction(transaction, true, true);
|
||||
return PendingTransaction.newPendingTransaction(transaction, true, true, MAX_SCORE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user