Skip to content

Commit

Permalink
Support code delegations when purging confirmed blocks in the layered…
Browse files Browse the repository at this point in the history
… txpool

Signed-off-by: Fabio Di Fabio <[email protected]>
  • Loading branch information
fab-10 committed Dec 11, 2024
1 parent ea04b25 commit f7f0833
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ public OptionalLong getNextNonceForSender(final Address sender) {
}

@Override
public synchronized void manageBlockAdded(
public void manageBlockAdded(
final BlockHeader blockHeader,
final List<Transaction> confirmedTransactions,
final List<Transaction> reorgTransactions,
Expand All @@ -447,19 +447,21 @@ public synchronized void manageBlockAdded(

final var reorgNonceRangeBySender = nonceRangeBySender(reorgTransactions);

try {
prioritizedTransactions.blockAdded(feeMarket, blockHeader, maxConfirmedNonceBySender);
} catch (final Throwable throwable) {
LOG.warn(
"Unexpected error {} when managing added block {}, maxNonceBySender {}, reorgNonceRangeBySender {}",
throwable,
blockHeader.toLogString(),
maxConfirmedNonceBySender,
reorgTransactions);
LOG.warn("Stack trace", throwable);
}
synchronized (this) {
try {
prioritizedTransactions.blockAdded(feeMarket, blockHeader, maxConfirmedNonceBySender);
} catch (final Throwable throwable) {
LOG.warn(
"Unexpected error {} when managing added block {}, maxNonceBySender {}, reorgNonceRangeBySender {}",
throwable,
blockHeader.toLogString(),
maxConfirmedNonceBySender,
reorgTransactions);
LOG.warn("Stack trace", throwable);
}

logBlockHeaderForReplay(blockHeader, maxConfirmedNonceBySender, reorgNonceRangeBySender);
logBlockHeaderForReplay(blockHeader, maxConfirmedNonceBySender, reorgNonceRangeBySender);
}
}

private void logBlockHeaderForReplay(
Expand Down Expand Up @@ -498,10 +500,25 @@ private void logBlockHeaderForReplay(
}

private Map<Address, Long> maxNonceBySender(final List<Transaction> confirmedTransactions) {
record SenderNonce(Address sender, long nonce) {}

return confirmedTransactions.stream()
.<SenderNonce>mapMulti(
(transaction, consumer) -> {
// always consider the sender
consumer.accept(new SenderNonce(transaction.getSender(), transaction.getNonce()));

// and if a code delegation tx also the authorities
if (transaction.getType().supportsDelegateCode()) {
transaction.getCodeDelegationList().get().stream()
.map(cd -> cd.authorizer().map(address -> new SenderNonce(address, cd.nonce())))
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(consumer);
}
})
.collect(
groupingBy(
Transaction::getSender, mapping(Transaction::getNonce, reducing(0L, Math::max))));
groupingBy(SenderNonce::sender, mapping(SenderNonce::nonce, reducing(0L, Math::max))));
}

private Map<Address, LongRange> nonceRangeBySender(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
@Test
public void toSize() {
TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0);
prepareTransaction(
TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0, List.of());
Transaction txTo =
preparedTx.to(Optional.of(Address.extract(Bytes32.random()))).createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
Expand Down Expand Up @@ -187,7 +188,8 @@ public void toSize() {
public void payloadSize() {

TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0);
prepareTransaction(
TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0, List.of());
Transaction txPayload = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txPayload.writeTo(rlpOut);
Expand Down Expand Up @@ -277,7 +279,7 @@ private void blobsWithCommitmentsFieldSize(
final long containerSize,
final long itemSize) {
TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1);
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1, List.of());
Transaction txBlob = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
TransactionEncoder.encodeRLP(txBlob, rlpOut, EncodingContext.POOLED_TRANSACTION);
Expand Down Expand Up @@ -309,7 +311,7 @@ private void blobsWithCommitmentsFieldSize(
@Test
public void blobsWithCommitmentsSize() {
TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1);
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1, List.of());
Transaction txBlob = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
TransactionEncoder.encodeRLP(txBlob, rlpOut, EncodingContext.POOLED_TRANSACTION);
Expand Down Expand Up @@ -337,7 +339,8 @@ public void blobsWithCommitmentsSize() {
public void pendingTransactionSize() {

TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0);
prepareTransaction(
TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0, List.of());
Transaction txPayload = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txPayload.writeTo(rlpOut);
Expand Down Expand Up @@ -369,7 +372,7 @@ public void accessListSize() {
final List<AccessListEntry> ales = List.of(ale1);

TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.ACCESS_LIST, 0, Wei.of(500), Wei.ZERO, 0, 0);
prepareTransaction(TransactionType.ACCESS_LIST, 0, Wei.of(500), Wei.ZERO, 0, 0, List.of());
Transaction txAccessList = preparedTx.accessList(ales).createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txAccessList.writeTo(rlpOut);
Expand Down Expand Up @@ -416,7 +419,14 @@ public void codeDelegationListSize() {
System.setProperty("jol.magicFieldOffset", "true");

TransactionTestFixture preparedTx =
prepareTransaction(TransactionType.DELEGATE_CODE, 0, Wei.of(500), Wei.ZERO, 0, 0);
prepareTransaction(
TransactionType.DELEGATE_CODE,
0,
Wei.of(500),
Wei.ZERO,
0,
0,
List.of(CODE_DELEGATION_SENDER_1));
Transaction txDelegateCode = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txDelegateCode.writeTo(rlpOut);
Expand Down Expand Up @@ -461,7 +471,7 @@ public void baseEIP1559AndEIP4844TransactionMemorySize() {
@Test
public void baseFrontierAndAccessListTransactionMemorySize() {
final Transaction txFrontier =
createTransaction(TransactionType.FRONTIER, 1, Wei.of(500), 0, KEYS1);
createTransaction(TransactionType.FRONTIER, 1, Wei.of(500), 0, List.of(), KEYS1);
assertThat(baseTransactionMemorySize(txFrontier, FRONTIER_ACCESS_LIST_CONSTANT_FIELD_PATHS))
.isEqualTo(FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ protected Transaction createTransactionReplacement(
originalTransaction.getMaxGasPrice().multiply(2).divide(10),
originalTransaction.getPayload().size(),
originalTransaction.getBlobCount(),
originalTransaction.getCodeDelegationList().orElse(List.of()),
keys);
}

Expand Down Expand Up @@ -191,7 +192,7 @@ public void txWithEffectiveGasPriceBelowCurrentMineableMinGasPriceIsNotPrioritiz
final TransactionType type) {
final PendingTransaction lowGasPriceTx =
createRemotePendingTransaction(
createTransaction(type, 0, DEFAULT_MIN_GAS_PRICE, Wei.ONE, 0, 1, KEYS1));
createTransaction(type, 0, DEFAULT_MIN_GAS_PRICE, Wei.ONE, 0, 1, List.of(), KEYS1));
assertThat(prioritizeTransaction(lowGasPriceTx)).isEqualTo(DROPPED);
assertEvicted(lowGasPriceTx);
assertTransactionNotPrioritized(lowGasPriceTx);
Expand All @@ -217,6 +218,7 @@ public void shouldPrioritizePriorityFeeThenTimeAddedToPoolSameTypeTxs(
0,
DEFAULT_MIN_GAS_PRICE.add(1).multiply(20),
0,
List.of(),
SIGNATURE_ALGORITHM.get().generateKeyPair())))
.collect(Collectors.toUnmodifiableList());

Expand All @@ -238,6 +240,7 @@ public void maxNumberOfTxsForTypeIsEnforced() {
DEFAULT_MIN_GAS_PRICE.divide(10),
0,
1,
List.of(),
SIGNATURE_ALGORITHM.get().generateKeyPair());
addedTxs.add(tx);
assertThat(prioritizeTransaction(tx)).isEqualTo(ADDED);
Expand All @@ -251,6 +254,7 @@ public void maxNumberOfTxsForTypeIsEnforced() {
DEFAULT_MIN_GAS_PRICE.divide(10),
0,
1,
List.of(),
SIGNATURE_ALGORITHM.get().generateKeyPair());
assertThat(prioritizeTransaction(overflowTx)).isEqualTo(DROPPED);

Expand All @@ -272,6 +276,7 @@ public void maxNumberOfTxsForTypeWithReplacement() {
DEFAULT_MIN_GAS_PRICE.divide(10),
0,
1,
List.of(),
KEYS1);
addedTxs.add(tx);
assertThat(prioritizeTransaction(tx)).isEqualTo(ADDED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.crypto.CodeDelegationSignature;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Blob;
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
import org.hyperledger.besu.datatypes.CodeDelegation;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.KZGCommitment;
import org.hyperledger.besu.datatypes.KZGProof;
Expand All @@ -31,14 +33,18 @@
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationTransactionEncoder;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolMetrics;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.metrics.StubMetricsSystem;
import org.hyperledger.besu.testutil.DeterministicEthScheduler;

import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.IntStream;
Expand All @@ -56,6 +62,9 @@ public class BaseTransactionPoolTest {
protected static final KeyPair KEYS2 = SIGNATURE_ALGORITHM.get().generateKeyPair();
protected static final Address SENDER1 = Util.publicKeyToAddress(KEYS1.getPublicKey());
protected static final Address SENDER2 = Util.publicKeyToAddress(KEYS2.getPublicKey());
protected static final CodeDelegation CODE_DELEGATION_SENDER_1 =
new org.hyperledger.besu.ethereum.core.CodeDelegation(
BigInteger.ZERO, Address.ZERO, 0, createCodeDelegationSignature(KEYS1, 0));
protected static final Wei DEFAULT_MIN_GAS_PRICE = Wei.of(50);
protected static final Wei DEFAULT_MIN_PRIORITY_FEE = Wei.ZERO;
private static final Random randomizeTxType = new Random();
Expand Down Expand Up @@ -92,7 +101,12 @@ protected Transaction createTransaction(
protected Transaction createEIP1559Transaction(
final long nonce, final KeyPair keys, final int gasFeeMultiplier) {
return createTransaction(
TransactionType.EIP1559, nonce, Wei.of(5000L).multiply(gasFeeMultiplier), 0, keys);
TransactionType.EIP1559,
nonce,
Wei.of(5000L).multiply(gasFeeMultiplier),
0,
List.of(),
keys);
}

protected Transaction createEIP4844Transaction(
Expand All @@ -104,6 +118,21 @@ protected Transaction createEIP4844Transaction(
Wei.of(5000L).multiply(gasFeeMultiplier).divide(10),
0,
blobCount,
List.of(),
keys);
}

protected Transaction createEIP7702Transaction(
final long nonce,
final KeyPair keys,
final int gasFeeMultiplier,
final List<CodeDelegation> codeDelegations) {
return createTransaction(
TransactionType.DELEGATE_CODE,
nonce,
Wei.of(5000L).multiply(gasFeeMultiplier),
0,
codeDelegations,
keys);
}

Expand All @@ -115,11 +144,12 @@ protected Transaction createTransactionOfSize(
randomizeTxType.nextInt(txSize < blobTransaction0.getSize() ? 3 : 4)];

final Transaction baseTx =
createTransaction(txType, nonce, maxGasPrice, maxGasPrice.divide(10), 0, 1, keys);
createTransaction(
txType, nonce, maxGasPrice, maxGasPrice.divide(10), 0, 1, List.of(), keys);
final int payloadSize = txSize - baseTx.getSize();

return createTransaction(
txType, nonce, maxGasPrice, maxGasPrice.divide(10), payloadSize, 1, keys);
txType, nonce, maxGasPrice, maxGasPrice.divide(10), payloadSize, 1, List.of(), keys);
}

protected Transaction createTransaction(
Expand All @@ -128,11 +158,14 @@ protected Transaction createTransaction(
final TransactionType txType = TransactionType.values()[randomizeTxType.nextInt(4)];

return switch (txType) {
case FRONTIER, ACCESS_LIST, EIP1559, DELEGATE_CODE ->
createTransaction(txType, nonce, maxGasPrice, payloadSize, keys);
case FRONTIER, ACCESS_LIST, EIP1559 ->
createTransaction(txType, nonce, maxGasPrice, payloadSize, List.of(), keys);
case BLOB ->
createTransaction(
txType, nonce, maxGasPrice, maxGasPrice.divide(10), payloadSize, 1, keys);
txType, nonce, maxGasPrice, maxGasPrice.divide(10), payloadSize, 1, List.of(), keys);
case DELEGATE_CODE ->
createTransaction(
txType, nonce, maxGasPrice, payloadSize, List.of(CODE_DELEGATION_SENDER_1), keys);
};
}

Expand All @@ -141,9 +174,10 @@ protected Transaction createTransaction(
final long nonce,
final Wei maxGasPrice,
final int payloadSize,
final List<CodeDelegation> codeDelegations,
final KeyPair keys) {
return createTransaction(
type, nonce, maxGasPrice, maxGasPrice.divide(10), payloadSize, 0, keys);
type, nonce, maxGasPrice, maxGasPrice.divide(10), payloadSize, 0, codeDelegations, keys);
}

protected Transaction createTransaction(
Expand All @@ -153,9 +187,10 @@ protected Transaction createTransaction(
final Wei maxPriorityFeePerGas,
final int payloadSize,
final int blobCount,
final List<CodeDelegation> codeDelegations,
final KeyPair keys) {
return prepareTransaction(
type, nonce, maxGasPrice, maxPriorityFeePerGas, payloadSize, blobCount)
type, nonce, maxGasPrice, maxPriorityFeePerGas, payloadSize, blobCount, codeDelegations)
.createTransaction(keys);
}

Expand All @@ -165,7 +200,8 @@ protected TransactionTestFixture prepareTransaction(
final Wei maxGasPrice,
final Wei maxPriorityFeePerGas,
final int payloadSize,
final int blobCount) {
final int blobCount,
final List<CodeDelegation> codeDelegations) {

var tx =
new TransactionTestFixture()
Expand Down Expand Up @@ -198,6 +234,8 @@ protected TransactionTestFixture prepareTransaction(
final var blobsWithCommitments =
new BlobsWithCommitments(kgzCommitments, blobs, kzgProofs, versionHashes);
tx.blobsWithCommitments(Optional.of(blobsWithCommitments));
} else if (type.supportsDelegateCode()) {
tx.codeDelegations(codeDelegations);
}
} else {
tx.gasPrice(maxGasPrice);
Expand All @@ -214,6 +252,7 @@ protected Transaction createTransactionReplacement(
originalTransaction.getMaxGasPrice().multiply(2).divide(10),
0,
1,
originalTransaction.getCodeDelegationList().orElse(List.of()),
keys);
}

Expand Down Expand Up @@ -269,4 +308,22 @@ protected long getRemovedCount(
return metricsSystem.getCounterValue(
TransactionPoolMetrics.REMOVED_COUNTER_NAME, source, priority, operation, layer);
}

protected static CodeDelegationSignature createCodeDelegationSignature(
final KeyPair keys, final long nonce) {
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
CodeDelegationTransactionEncoder.encodeSingleCodeDelegationWithoutSignature(
new org.hyperledger.besu.ethereum.core.CodeDelegation(
BigInteger.ZERO, Address.ZERO, nonce, null),
rlpOutput);

final Hash hash =
Hash.hash(
Bytes.concatenate(
org.hyperledger.besu.ethereum.core.CodeDelegation.MAGIC, rlpOutput.encoded()));

final var signature = SIGNATURE_ALGORITHM.get().sign(hash, keys);

return CodeDelegationSignature.create(signature.getR(), signature.getS(), signature.getRecId());
}
}
Loading

0 comments on commit f7f0833

Please sign in to comment.