Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.

Commit 4a97d57

Browse files
fab-10garyschulte
authored andcommitted
Support code delegations when purging confirmed blocks in the layered txpool (#8018)
Signed-off-by: Fabio Di Fabio <[email protected]>
1 parent aec8138 commit 4a97d57

File tree

8 files changed

+315
-68
lines changed

8 files changed

+315
-68
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
- Retrieve all transaction receipts for a block in one request [#6646](https://github.com/hyperledger/besu/pull/6646)
2020
- Implement EIP-7840: Add blob schedule to config files [#8042](https://github.com/hyperledger/besu/pull/8042)
2121
- Allow gasPrice (legacy) and 1559 gasPrice params to be specified simultaneously for `eth_call`, `eth_createAccessList`, and `eth_estimateGas` [#8059](https://github.com/hyperledger/besu/pull/8059)
22-
22+
- Add support for EIP-7702 transaction in the txpool [#8018](https://github.com/hyperledger/besu/pull/8018) [#7984](https://github.com/hyperledger/besu/pull/7984)
2323

2424
### Bug fixes
2525
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)

ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,35 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.core;
1616

17+
import org.hyperledger.besu.crypto.CodeDelegationSignature;
1718
import org.hyperledger.besu.crypto.KeyPair;
18-
import org.hyperledger.besu.crypto.SECPSignature;
19+
import org.hyperledger.besu.crypto.SignatureAlgorithm;
20+
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
1921
import org.hyperledger.besu.datatypes.AccessListEntry;
2022
import org.hyperledger.besu.datatypes.Address;
2123
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
24+
import org.hyperledger.besu.datatypes.Hash;
2225
import org.hyperledger.besu.datatypes.TransactionType;
2326
import org.hyperledger.besu.datatypes.VersionedHash;
2427
import org.hyperledger.besu.datatypes.Wei;
28+
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationTransactionEncoder;
29+
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
2530

2631
import java.math.BigInteger;
2732
import java.util.List;
2833
import java.util.Optional;
2934

35+
import com.google.common.base.Supplier;
36+
import com.google.common.base.Suppliers;
3037
import org.apache.tuweni.bytes.Bytes;
3138

3239
public class TransactionTestFixture {
33-
private final SECPSignature signature =
34-
new SECPSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
40+
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
41+
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
42+
private static final KeyPair KEY_PAIR = SIGNATURE_ALGORITHM.get().generateKeyPair();
43+
private static final org.hyperledger.besu.datatypes.CodeDelegation CODE_DELEGATION =
44+
createSignedCodeDelegation(BigInteger.ZERO, Address.ZERO, 0, KEY_PAIR);
45+
3546
private TransactionType transactionType = TransactionType.FRONTIER;
3647

3748
private long nonce = 0;
@@ -100,9 +111,7 @@ public Transaction createTransaction(final KeyPair keys) {
100111
builder.maxPriorityFeePerGas(maxPriorityFeePerGas.orElse(Wei.of(500)));
101112
builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000)));
102113
builder.accessList(accessListEntries.orElse(List.of()));
103-
builder.codeDelegations(
104-
codeDelegations.orElse(
105-
List.of(new CodeDelegation(chainId.get(), sender, 0, signature))));
114+
builder.codeDelegations(codeDelegations.orElse(List.of(CODE_DELEGATION)));
106115
break;
107116
}
108117

@@ -196,7 +205,28 @@ public TransactionTestFixture blobsWithCommitments(final Optional<BlobsWithCommi
196205

197206
public TransactionTestFixture codeDelegations(
198207
final List<org.hyperledger.besu.datatypes.CodeDelegation> codeDelegations) {
199-
this.codeDelegations = Optional.of(codeDelegations);
208+
this.codeDelegations = Optional.ofNullable(codeDelegations);
200209
return this;
201210
}
211+
212+
public static CodeDelegation createSignedCodeDelegation(
213+
final BigInteger chainId, final Address address, final long nonce, final KeyPair keys) {
214+
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
215+
CodeDelegationTransactionEncoder.encodeSingleCodeDelegationWithoutSignature(
216+
new org.hyperledger.besu.ethereum.core.CodeDelegation(chainId, address, nonce, null),
217+
rlpOutput);
218+
219+
final Hash hash =
220+
Hash.hash(
221+
Bytes.concatenate(
222+
org.hyperledger.besu.ethereum.core.CodeDelegation.MAGIC, rlpOutput.encoded()));
223+
224+
final var signature = SIGNATURE_ALGORITHM.get().sign(hash, keys);
225+
226+
return new org.hyperledger.besu.ethereum.core.CodeDelegation(
227+
chainId,
228+
address,
229+
nonce,
230+
CodeDelegationSignature.create(signature.getR(), signature.getS(), signature.getRecId()));
231+
}
202232
}

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ public interface MemorySize {
430430
int ACCESS_LIST_ENTRY_SHALLOW_SIZE = 248;
431431
int OPTIONAL_ACCESS_LIST_SHALLOW_SIZE = 40;
432432
int OPTIONAL_CODE_DELEGATION_LIST_SHALLOW_SIZE = 40;
433-
int CODE_DELEGATION_ENTRY_SIZE = 472;
433+
int CODE_DELEGATION_ENTRY_SIZE = 520;
434434
int VERSIONED_HASH_SIZE = 96;
435435
int LIST_SHALLOW_SIZE = 48;
436436
int OPTIONAL_SHALLOW_SIZE = 16;

ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ public OptionalLong getNextNonceForSender(final Address sender) {
433433
}
434434

435435
@Override
436-
public synchronized void manageBlockAdded(
436+
public void manageBlockAdded(
437437
final BlockHeader blockHeader,
438438
final List<Transaction> confirmedTransactions,
439439
final List<Transaction> reorgTransactions,
@@ -447,19 +447,21 @@ public synchronized void manageBlockAdded(
447447

448448
final var reorgNonceRangeBySender = nonceRangeBySender(reorgTransactions);
449449

450-
try {
451-
prioritizedTransactions.blockAdded(feeMarket, blockHeader, maxConfirmedNonceBySender);
452-
} catch (final Throwable throwable) {
453-
LOG.warn(
454-
"Unexpected error {} when managing added block {}, maxNonceBySender {}, reorgNonceRangeBySender {}",
455-
throwable,
456-
blockHeader.toLogString(),
457-
maxConfirmedNonceBySender,
458-
reorgTransactions);
459-
LOG.warn("Stack trace", throwable);
460-
}
450+
synchronized (this) {
451+
try {
452+
prioritizedTransactions.blockAdded(feeMarket, blockHeader, maxConfirmedNonceBySender);
453+
} catch (final Throwable throwable) {
454+
LOG.warn(
455+
"Unexpected error {} when managing added block {}, maxNonceBySender {}, reorgNonceRangeBySender {}",
456+
throwable,
457+
blockHeader.toLogString(),
458+
maxConfirmedNonceBySender,
459+
reorgTransactions);
460+
LOG.warn("Stack trace", throwable);
461+
}
461462

462-
logBlockHeaderForReplay(blockHeader, maxConfirmedNonceBySender, reorgNonceRangeBySender);
463+
logBlockHeaderForReplay(blockHeader, maxConfirmedNonceBySender, reorgNonceRangeBySender);
464+
}
463465
}
464466

465467
private void logBlockHeaderForReplay(
@@ -498,10 +500,25 @@ private void logBlockHeaderForReplay(
498500
}
499501

500502
private Map<Address, Long> maxNonceBySender(final List<Transaction> confirmedTransactions) {
503+
record SenderNonce(Address sender, long nonce) {}
504+
501505
return confirmedTransactions.stream()
506+
.<SenderNonce>mapMulti(
507+
(transaction, consumer) -> {
508+
// always consider the sender
509+
consumer.accept(new SenderNonce(transaction.getSender(), transaction.getNonce()));
510+
511+
// and if a code delegation tx also the authorities
512+
if (transaction.getType().supportsDelegateCode()) {
513+
transaction.getCodeDelegationList().get().stream()
514+
.map(cd -> cd.authorizer().map(address -> new SenderNonce(address, cd.nonce())))
515+
.filter(Optional::isPresent)
516+
.map(Optional::get)
517+
.forEach(consumer);
518+
}
519+
})
502520
.collect(
503-
groupingBy(
504-
Transaction::getSender, mapping(Transaction::getNonce, reducing(0L, Math::max))));
521+
groupingBy(SenderNonce::sender, mapping(SenderNonce::nonce, reducing(0L, Math::max))));
505522
}
506523

507524
private Map<Address, LongRange> nonceRangeBySender(

ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionEstimatedMemorySizeTest.java

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
140140
@Test
141141
public void toSize() {
142142
TransactionTestFixture preparedTx =
143-
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0);
143+
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0, null);
144144
Transaction txTo =
145145
preparedTx.to(Optional.of(Address.extract(Bytes32.random()))).createTransaction(KEYS1);
146146
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
@@ -187,7 +187,7 @@ public void toSize() {
187187
public void payloadSize() {
188188

189189
TransactionTestFixture preparedTx =
190-
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0);
190+
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0, null);
191191
Transaction txPayload = preparedTx.createTransaction(KEYS1);
192192
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
193193
txPayload.writeTo(rlpOut);
@@ -277,7 +277,7 @@ private void blobsWithCommitmentsFieldSize(
277277
final long containerSize,
278278
final long itemSize) {
279279
TransactionTestFixture preparedTx =
280-
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1);
280+
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1, null);
281281
Transaction txBlob = preparedTx.createTransaction(KEYS1);
282282
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
283283
TransactionEncoder.encodeRLP(txBlob, rlpOut, EncodingContext.POOLED_TRANSACTION);
@@ -309,7 +309,7 @@ private void blobsWithCommitmentsFieldSize(
309309
@Test
310310
public void blobsWithCommitmentsSize() {
311311
TransactionTestFixture preparedTx =
312-
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1);
312+
prepareTransaction(TransactionType.BLOB, 10, Wei.of(500), Wei.of(50), 10, 1, null);
313313
Transaction txBlob = preparedTx.createTransaction(KEYS1);
314314
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
315315
TransactionEncoder.encodeRLP(txBlob, rlpOut, EncodingContext.POOLED_TRANSACTION);
@@ -337,7 +337,7 @@ public void blobsWithCommitmentsSize() {
337337
public void pendingTransactionSize() {
338338

339339
TransactionTestFixture preparedTx =
340-
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0);
340+
prepareTransaction(TransactionType.ACCESS_LIST, 10, Wei.of(500), Wei.ZERO, 10, 0, null);
341341
Transaction txPayload = preparedTx.createTransaction(KEYS1);
342342
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
343343
txPayload.writeTo(rlpOut);
@@ -369,7 +369,7 @@ public void accessListSize() {
369369
final List<AccessListEntry> ales = List.of(ale1);
370370

371371
TransactionTestFixture preparedTx =
372-
prepareTransaction(TransactionType.ACCESS_LIST, 0, Wei.of(500), Wei.ZERO, 0, 0);
372+
prepareTransaction(TransactionType.ACCESS_LIST, 0, Wei.of(500), Wei.ZERO, 0, 0, null);
373373
Transaction txAccessList = preparedTx.accessList(ales).createTransaction(KEYS1);
374374
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
375375
txAccessList.writeTo(rlpOut);
@@ -416,7 +416,14 @@ public void codeDelegationListSize() {
416416
System.setProperty("jol.magicFieldOffset", "true");
417417

418418
TransactionTestFixture preparedTx =
419-
prepareTransaction(TransactionType.DELEGATE_CODE, 0, Wei.of(500), Wei.ZERO, 0, 0);
419+
prepareTransaction(
420+
TransactionType.DELEGATE_CODE,
421+
0,
422+
Wei.of(500),
423+
Wei.ZERO,
424+
0,
425+
0,
426+
List.of(CODE_DELEGATION_SENDER_1));
420427
Transaction txDelegateCode = preparedTx.createTransaction(KEYS1);
421428
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
422429
txDelegateCode.writeTo(rlpOut);
@@ -461,7 +468,7 @@ public void baseEIP1559AndEIP4844TransactionMemorySize() {
461468
@Test
462469
public void baseFrontierAndAccessListTransactionMemorySize() {
463470
final Transaction txFrontier =
464-
createTransaction(TransactionType.FRONTIER, 1, Wei.of(500), 0, KEYS1);
471+
createTransaction(TransactionType.FRONTIER, 1, Wei.of(500), 0, List.of(), KEYS1);
465472
assertThat(baseTransactionMemorySize(txFrontier, FRONTIER_ACCESS_LIST_CONSTANT_FIELD_PATHS))
466473
.isEqualTo(FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE);
467474
}
@@ -575,15 +582,18 @@ private long sizeOfField(final Object container, final String... excludePaths) {
575582
*
576583
* @param filePath where to save the heap dump
577584
* @param live true to only include live objects
578-
* @throws IOException if any errors happen during the saving
579585
*/
580586
@SuppressWarnings("unused")
581-
private static void dumpHeap(final String filePath, final boolean live) throws IOException {
582-
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
583-
HotSpotDiagnosticMXBean mxBean =
584-
ManagementFactory.newPlatformMXBeanProxy(
585-
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
586-
mxBean.dumpHeap(filePath, live);
587+
private static void dumpHeap(final String filePath, final boolean live) {
588+
try {
589+
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
590+
HotSpotDiagnosticMXBean mxBean =
591+
ManagementFactory.newPlatformMXBeanProxy(
592+
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
593+
mxBean.dumpHeap(filePath, live);
594+
} catch (IOException e) {
595+
throw new RuntimeException(e);
596+
}
587597
}
588598

589599
record FieldSize(String path, Class<?> clazz, long size) implements Comparable<FieldSize> {

ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/BaseFeePrioritizedTransactionsTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ protected Transaction createTransactionReplacement(
116116
originalTransaction.getMaxGasPrice().multiply(2).divide(10),
117117
originalTransaction.getPayload().size(),
118118
originalTransaction.getBlobCount(),
119+
originalTransaction.getCodeDelegationList().orElse(null),
119120
keys);
120121
}
121122

@@ -191,7 +192,7 @@ public void txWithEffectiveGasPriceBelowCurrentMineableMinGasPriceIsNotPrioritiz
191192
final TransactionType type) {
192193
final PendingTransaction lowGasPriceTx =
193194
createRemotePendingTransaction(
194-
createTransaction(type, 0, DEFAULT_MIN_GAS_PRICE, Wei.ONE, 0, 1, KEYS1));
195+
createTransaction(type, 0, DEFAULT_MIN_GAS_PRICE, Wei.ONE, 0, 1, null, KEYS1));
195196
assertThat(prioritizeTransaction(lowGasPriceTx)).isEqualTo(DROPPED);
196197
assertEvicted(lowGasPriceTx);
197198
assertTransactionNotPrioritized(lowGasPriceTx);
@@ -217,6 +218,7 @@ public void shouldPrioritizePriorityFeeThenTimeAddedToPoolSameTypeTxs(
217218
0,
218219
DEFAULT_MIN_GAS_PRICE.add(1).multiply(20),
219220
0,
221+
null,
220222
SIGNATURE_ALGORITHM.get().generateKeyPair())))
221223
.collect(Collectors.toUnmodifiableList());
222224

@@ -238,6 +240,7 @@ public void maxNumberOfTxsForTypeIsEnforced() {
238240
DEFAULT_MIN_GAS_PRICE.divide(10),
239241
0,
240242
1,
243+
null,
241244
SIGNATURE_ALGORITHM.get().generateKeyPair());
242245
addedTxs.add(tx);
243246
assertThat(prioritizeTransaction(tx)).isEqualTo(ADDED);
@@ -251,6 +254,7 @@ public void maxNumberOfTxsForTypeIsEnforced() {
251254
DEFAULT_MIN_GAS_PRICE.divide(10),
252255
0,
253256
1,
257+
null,
254258
SIGNATURE_ALGORITHM.get().generateKeyPair());
255259
assertThat(prioritizeTransaction(overflowTx)).isEqualTo(DROPPED);
256260

@@ -272,6 +276,7 @@ public void maxNumberOfTxsForTypeWithReplacement() {
272276
DEFAULT_MIN_GAS_PRICE.divide(10),
273277
0,
274278
1,
279+
null,
275280
KEYS1);
276281
addedTxs.add(tx);
277282
assertThat(prioritizeTransaction(tx)).isEqualTo(ADDED);

0 commit comments

Comments
 (0)