Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support code delegations when purging confirmed blocks in the layered txpool #8018

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

### Additions and Improvements
- Retrieve all transaction receipts for a block in one request [#6646](https://github.com/hyperledger/besu/pull/6646)
- 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)

### Bug fixes
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,35 @@
*/
package org.hyperledger.besu.ethereum.core;

import org.hyperledger.besu.crypto.CodeDelegationSignature;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationTransactionEncoder;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;

import java.math.BigInteger;
import java.util.List;
import java.util.Optional;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;

public class TransactionTestFixture {
private final SECPSignature signature =
new SECPSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair KEY_PAIR = SIGNATURE_ALGORITHM.get().generateKeyPair();
private static final org.hyperledger.besu.datatypes.CodeDelegation CODE_DELEGATION =
createSignedCodeDelegation(BigInteger.ZERO, Address.ZERO, 0, KEY_PAIR);

private TransactionType transactionType = TransactionType.FRONTIER;

private long nonce = 0;
Expand Down Expand Up @@ -100,9 +111,7 @@ public Transaction createTransaction(final KeyPair keys) {
builder.maxPriorityFeePerGas(maxPriorityFeePerGas.orElse(Wei.of(500)));
builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000)));
builder.accessList(accessListEntries.orElse(List.of()));
builder.codeDelegations(
codeDelegations.orElse(
List.of(new CodeDelegation(chainId.get(), sender, 0, signature))));
builder.codeDelegations(codeDelegations.orElse(List.of(CODE_DELEGATION)));
break;
}

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

public TransactionTestFixture codeDelegations(
final List<org.hyperledger.besu.datatypes.CodeDelegation> codeDelegations) {
this.codeDelegations = Optional.of(codeDelegations);
this.codeDelegations = Optional.ofNullable(codeDelegations);
return this;
}

public static CodeDelegation createSignedCodeDelegation(
final BigInteger chainId, final Address address, final long nonce, final KeyPair keys) {
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
CodeDelegationTransactionEncoder.encodeSingleCodeDelegationWithoutSignature(
new org.hyperledger.besu.ethereum.core.CodeDelegation(chainId, address, 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 new org.hyperledger.besu.ethereum.core.CodeDelegation(
chainId,
address,
nonce,
CodeDelegationSignature.create(signature.getR(), signature.getS(), signature.getRecId()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ public interface MemorySize {
int ACCESS_LIST_ENTRY_SHALLOW_SIZE = 248;
int OPTIONAL_ACCESS_LIST_SHALLOW_SIZE = 40;
int OPTIONAL_CODE_DELEGATION_LIST_SHALLOW_SIZE = 40;
int CODE_DELEGATION_ENTRY_SIZE = 472;
int CODE_DELEGATION_ENTRY_SIZE = 520;
int VERSIONED_HASH_SIZE = 96;
int LIST_SHALLOW_SIZE = 48;
int OPTIONAL_SHALLOW_SIZE = 16;
Expand Down
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,7 @@ 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, null);
Transaction txTo =
preparedTx.to(Optional.of(Address.extract(Bytes32.random()))).createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
Expand Down Expand Up @@ -187,7 +187,7 @@ 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, null);
Transaction txPayload = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txPayload.writeTo(rlpOut);
Expand Down Expand Up @@ -277,7 +277,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, null);
Transaction txBlob = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
TransactionEncoder.encodeRLP(txBlob, rlpOut, EncodingContext.POOLED_TRANSACTION);
Expand Down Expand Up @@ -309,7 +309,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, null);
Transaction txBlob = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
TransactionEncoder.encodeRLP(txBlob, rlpOut, EncodingContext.POOLED_TRANSACTION);
Expand Down Expand Up @@ -337,7 +337,7 @@ 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, null);
Transaction txPayload = preparedTx.createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txPayload.writeTo(rlpOut);
Expand Down Expand Up @@ -369,7 +369,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, null);
Transaction txAccessList = preparedTx.accessList(ales).createTransaction(KEYS1);
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
txAccessList.writeTo(rlpOut);
Expand Down Expand Up @@ -416,7 +416,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 +468,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 Expand Up @@ -575,15 +582,18 @@ private long sizeOfField(final Object container, final String... excludePaths) {
*
* @param filePath where to save the heap dump
* @param live true to only include live objects
* @throws IOException if any errors happen during the saving
*/
@SuppressWarnings("unused")
private static void dumpHeap(final String filePath, final boolean live) throws IOException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean =
ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
private static void dumpHeap(final String filePath, final boolean live) {
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean =
ManagementFactory.newPlatformMXBeanProxy(
server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap(filePath, live);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

record FieldSize(String path, Class<?> clazz, long size) implements Comparable<FieldSize> {
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(null),
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, null, 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,
null,
SIGNATURE_ALGORITHM.get().generateKeyPair())))
.collect(Collectors.toUnmodifiableList());

Expand All @@ -238,6 +240,7 @@ public void maxNumberOfTxsForTypeIsEnforced() {
DEFAULT_MIN_GAS_PRICE.divide(10),
0,
1,
null,
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,
null,
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,
null,
KEYS1);
addedTxs.add(tx);
assertThat(prioritizeTransaction(tx)).isEqualTo(ADDED);
Expand Down
Loading
Loading