mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 15:37:54 -05:00
Estimate the memory size of EIP-7702 transactions (#7984)
* Estimate the memory size of EIP-7702 transactions Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
This commit is contained in:
@@ -1092,6 +1092,10 @@ public class Transaction
|
||||
blobsWithCommitments.map(
|
||||
withCommitments ->
|
||||
blobsWithCommitmentsDetachedCopy(withCommitments, detachedVersionedHashes.get()));
|
||||
final Optional<List<CodeDelegation>> detachedCodeDelegationList =
|
||||
maybeCodeDelegationList.map(
|
||||
codeDelegations ->
|
||||
codeDelegations.stream().map(this::codeDelegationDetachedCopy).toList());
|
||||
|
||||
final var copiedTx =
|
||||
new Transaction(
|
||||
@@ -1112,7 +1116,7 @@ public class Transaction
|
||||
chainId,
|
||||
detachedVersionedHashes,
|
||||
detachedBlobsWithCommitments,
|
||||
maybeCodeDelegationList);
|
||||
detachedCodeDelegationList);
|
||||
|
||||
// copy also the computed fields, to avoid to recompute them
|
||||
copiedTx.sender = this.sender;
|
||||
@@ -1129,6 +1133,15 @@ public class Transaction
|
||||
return new AccessListEntry(detachedAddress, detachedStorage);
|
||||
}
|
||||
|
||||
private CodeDelegation codeDelegationDetachedCopy(final CodeDelegation codeDelegation) {
|
||||
final Address detachedAddress = Address.wrap(codeDelegation.address().copy());
|
||||
return new org.hyperledger.besu.ethereum.core.CodeDelegation(
|
||||
codeDelegation.chainId(),
|
||||
detachedAddress,
|
||||
codeDelegation.nonce(),
|
||||
codeDelegation.signature());
|
||||
}
|
||||
|
||||
private BlobsWithCommitments blobsWithCommitmentsDetachedCopy(
|
||||
final BlobsWithCommitments blobsWithCommitments, final List<VersionedHash> versionedHashes) {
|
||||
final var detachedCommitments =
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.core;
|
||||
|
||||
import org.hyperledger.besu.crypto.KeyPair;
|
||||
import org.hyperledger.besu.crypto.SECPSignature;
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
@@ -29,7 +30,8 @@ import java.util.Optional;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class TransactionTestFixture {
|
||||
|
||||
private final SECPSignature signature =
|
||||
new SECPSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
|
||||
private TransactionType transactionType = TransactionType.FRONTIER;
|
||||
|
||||
private long nonce = 0;
|
||||
@@ -56,6 +58,8 @@ public class TransactionTestFixture {
|
||||
|
||||
private Optional<BlobsWithCommitments> blobs = Optional.empty();
|
||||
private Optional<BigInteger> v = Optional.empty();
|
||||
private Optional<List<org.hyperledger.besu.datatypes.CodeDelegation>> codeDelegations =
|
||||
Optional.empty();
|
||||
|
||||
public Transaction createTransaction(final KeyPair keys) {
|
||||
final Transaction.Builder builder = Transaction.builder();
|
||||
@@ -93,6 +97,12 @@ public class TransactionTestFixture {
|
||||
}
|
||||
break;
|
||||
case DELEGATE_CODE:
|
||||
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))));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -183,4 +193,10 @@ public class TransactionTestFixture {
|
||||
this.blobs = blobs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransactionTestFixture codeDelegations(
|
||||
final List<org.hyperledger.besu.datatypes.CodeDelegation> codeDelegations) {
|
||||
this.codeDelegations = Optional.of(codeDelegations);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,24 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.eth.transactions;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.ACCESS_LIST_ENTRY_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.ACCESS_LIST_STORAGE_KEY_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.BLOBS_WITH_COMMITMENTS_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.BLOB_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.CODE_DELEGATION_ENTRY_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.EIP1559_AND_EIP4844_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.KZG_COMMITMENT_OR_PROOF_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_ACCESS_LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_CHAIN_ID_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_CODE_DELEGATION_LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_TO_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.PAYLOAD_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.PENDING_TRANSACTION_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.VERSIONED_HASH_SIZE;
|
||||
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
@@ -31,21 +49,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
public abstract class PendingTransaction
|
||||
implements org.hyperledger.besu.datatypes.PendingTransaction {
|
||||
static final int NOT_INITIALIZED = -1;
|
||||
static final int FRONTIER_AND_ACCESS_LIST_SHALLOW_MEMORY_SIZE = 904;
|
||||
static final int EIP1559_AND_EIP4844_SHALLOW_MEMORY_SIZE = 1016;
|
||||
static final int OPTIONAL_TO_MEMORY_SIZE = 112;
|
||||
static final int OPTIONAL_CHAIN_ID_MEMORY_SIZE = 80;
|
||||
static final int PAYLOAD_BASE_MEMORY_SIZE = 32;
|
||||
static final int ACCESS_LIST_STORAGE_KEY_MEMORY_SIZE = 32;
|
||||
static final int ACCESS_LIST_ENTRY_BASE_MEMORY_SIZE = 248;
|
||||
static final int OPTIONAL_ACCESS_LIST_MEMORY_SIZE = 24;
|
||||
static final int VERSIONED_HASH_SIZE = 96;
|
||||
static final int BASE_LIST_SIZE = 48;
|
||||
static final int BASE_OPTIONAL_SIZE = 16;
|
||||
static final int KZG_COMMITMENT_OR_PROOF_SIZE = 112;
|
||||
static final int BLOB_SIZE = 131136;
|
||||
static final int BLOBS_WITH_COMMITMENTS_SIZE = 40;
|
||||
static final int PENDING_TRANSACTION_MEMORY_SIZE = 40;
|
||||
private static final AtomicLong TRANSACTIONS_ADDED = new AtomicLong();
|
||||
private final Transaction transaction;
|
||||
private final long addedAt;
|
||||
@@ -147,20 +150,20 @@ public abstract class PendingTransaction
|
||||
case ACCESS_LIST -> computeAccessListMemorySize();
|
||||
case EIP1559 -> computeEIP1559MemorySize();
|
||||
case BLOB -> computeBlobMemorySize();
|
||||
case DELEGATE_CODE -> computeSetCodeMemorySize();
|
||||
case DELEGATE_CODE -> computeDelegateCodeMemorySize();
|
||||
}
|
||||
+ PENDING_TRANSACTION_MEMORY_SIZE;
|
||||
+ PENDING_TRANSACTION_SHALLOW_SIZE;
|
||||
}
|
||||
|
||||
private int computeFrontierMemorySize() {
|
||||
return FRONTIER_AND_ACCESS_LIST_SHALLOW_MEMORY_SIZE
|
||||
return FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE
|
||||
+ computePayloadMemorySize()
|
||||
+ computeToMemorySize()
|
||||
+ computeChainIdMemorySize();
|
||||
}
|
||||
|
||||
private int computeAccessListMemorySize() {
|
||||
return FRONTIER_AND_ACCESS_LIST_SHALLOW_MEMORY_SIZE
|
||||
return FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE
|
||||
+ computePayloadMemorySize()
|
||||
+ computeToMemorySize()
|
||||
+ computeChainIdMemorySize()
|
||||
@@ -168,7 +171,7 @@ public abstract class PendingTransaction
|
||||
}
|
||||
|
||||
private int computeEIP1559MemorySize() {
|
||||
return EIP1559_AND_EIP4844_SHALLOW_MEMORY_SIZE
|
||||
return EIP1559_AND_EIP4844_SHALLOW_SIZE
|
||||
+ computePayloadMemorySize()
|
||||
+ computeToMemorySize()
|
||||
+ computeChainIdMemorySize()
|
||||
@@ -177,41 +180,41 @@ public abstract class PendingTransaction
|
||||
|
||||
private int computeBlobMemorySize() {
|
||||
return computeEIP1559MemorySize()
|
||||
+ BASE_OPTIONAL_SIZE // for the versionedHashes field
|
||||
+ OPTIONAL_SHALLOW_SIZE // for the versionedHashes field
|
||||
+ computeBlobWithCommitmentsMemorySize();
|
||||
}
|
||||
|
||||
private int computeSetCodeMemorySize() {
|
||||
return 0;
|
||||
private int computeDelegateCodeMemorySize() {
|
||||
return computeEIP1559MemorySize() + computeCodeDelegationListMemorySize();
|
||||
}
|
||||
|
||||
private int computeBlobWithCommitmentsMemorySize() {
|
||||
final int blobCount = transaction.getBlobCount();
|
||||
|
||||
return BASE_OPTIONAL_SIZE
|
||||
return OPTIONAL_SHALLOW_SIZE
|
||||
+ BLOBS_WITH_COMMITMENTS_SIZE
|
||||
+ (BASE_LIST_SIZE * 4)
|
||||
+ (LIST_SHALLOW_SIZE * 4)
|
||||
+ (KZG_COMMITMENT_OR_PROOF_SIZE * blobCount * 2)
|
||||
+ (VERSIONED_HASH_SIZE * blobCount)
|
||||
+ (BLOB_SIZE * blobCount);
|
||||
}
|
||||
|
||||
private int computePayloadMemorySize() {
|
||||
return transaction.getPayload().size() > 0
|
||||
? PAYLOAD_BASE_MEMORY_SIZE + transaction.getPayload().size()
|
||||
return !transaction.getPayload().isEmpty()
|
||||
? PAYLOAD_SHALLOW_SIZE + transaction.getPayload().size()
|
||||
: 0;
|
||||
}
|
||||
|
||||
private int computeToMemorySize() {
|
||||
if (transaction.getTo().isPresent()) {
|
||||
return OPTIONAL_TO_MEMORY_SIZE;
|
||||
return OPTIONAL_TO_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int computeChainIdMemorySize() {
|
||||
if (transaction.getChainId().isPresent()) {
|
||||
return OPTIONAL_CHAIN_ID_MEMORY_SIZE;
|
||||
return OPTIONAL_CHAIN_ID_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -221,11 +224,23 @@ public abstract class PendingTransaction
|
||||
.getAccessList()
|
||||
.map(
|
||||
al -> {
|
||||
int totalSize = OPTIONAL_ACCESS_LIST_MEMORY_SIZE;
|
||||
totalSize += al.size() * ACCESS_LIST_ENTRY_BASE_MEMORY_SIZE;
|
||||
int totalSize = OPTIONAL_ACCESS_LIST_SHALLOW_SIZE;
|
||||
totalSize += al.size() * ACCESS_LIST_ENTRY_SHALLOW_SIZE;
|
||||
totalSize +=
|
||||
al.stream().map(AccessListEntry::storageKeys).mapToInt(List::size).sum()
|
||||
* ACCESS_LIST_STORAGE_KEY_MEMORY_SIZE;
|
||||
* ACCESS_LIST_STORAGE_KEY_SIZE;
|
||||
return totalSize;
|
||||
})
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
private int computeCodeDelegationListMemorySize() {
|
||||
return transaction
|
||||
.getCodeDelegationList()
|
||||
.map(
|
||||
cd -> {
|
||||
int totalSize = OPTIONAL_CODE_DELEGATION_LIST_SHALLOW_SIZE;
|
||||
totalSize += cd.size() * CODE_DELEGATION_ENTRY_SIZE;
|
||||
return totalSize;
|
||||
})
|
||||
.orElse(0);
|
||||
@@ -252,7 +267,7 @@ public abstract class PendingTransaction
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * (int) (sequence ^ (sequence >>> 32));
|
||||
return 31 * Long.hashCode(sequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -399,4 +414,29 @@ public abstract class PendingTransaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The memory size of an object is calculated using the PendingTransactionEstimatedMemorySizeTest
|
||||
* look there for the details of the calculation and to adapt the code when any of the related
|
||||
* class changes its structure.
|
||||
*/
|
||||
public interface MemorySize {
|
||||
int FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE = 904;
|
||||
int EIP1559_AND_EIP4844_SHALLOW_SIZE = 1016;
|
||||
int OPTIONAL_TO_SIZE = 112;
|
||||
int OPTIONAL_CHAIN_ID_SIZE = 80;
|
||||
int PAYLOAD_SHALLOW_SIZE = 32;
|
||||
int ACCESS_LIST_STORAGE_KEY_SIZE = 32;
|
||||
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 = 432;
|
||||
int VERSIONED_HASH_SIZE = 96;
|
||||
int LIST_SHALLOW_SIZE = 48;
|
||||
int OPTIONAL_SHALLOW_SIZE = 16;
|
||||
int KZG_COMMITMENT_OR_PROOF_SIZE = 112;
|
||||
int BLOB_SIZE = 131136;
|
||||
int BLOBS_WITH_COMMITMENTS_SIZE = 40;
|
||||
int PENDING_TRANSACTION_SHALLOW_SIZE = 40;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,28 @@
|
||||
package org.hyperledger.besu.ethereum.eth.transactions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.ACCESS_LIST_ENTRY_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.ACCESS_LIST_STORAGE_KEY_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.BLOBS_WITH_COMMITMENTS_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.BLOB_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.CODE_DELEGATION_ENTRY_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.EIP1559_AND_EIP4844_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.KZG_COMMITMENT_OR_PROOF_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_ACCESS_LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_CHAIN_ID_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_CODE_DELEGATION_LIST_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.OPTIONAL_TO_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.PAYLOAD_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.PENDING_TRANSACTION_SHALLOW_SIZE;
|
||||
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.MemorySize.VERSIONED_HASH_SIZE;
|
||||
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
@@ -31,6 +48,8 @@ import org.hyperledger.besu.ethereum.eth.transactions.layered.BaseTransactionPoo
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@@ -41,8 +60,10 @@ import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.function.Function;
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -53,18 +74,68 @@ import org.openjdk.jol.info.GraphPathRecord;
|
||||
import org.openjdk.jol.info.GraphVisitor;
|
||||
import org.openjdk.jol.info.GraphWalker;
|
||||
|
||||
@EnabledOnOs(OS.LINUX)
|
||||
/**
|
||||
* This test class has a double utility, first it is used to verify that the current memory size of
|
||||
* a pending transaction object correspond to the calculated values, so if any of the tests in this
|
||||
* file fails, it probably means that one of the related classes has changed its format, and a new
|
||||
* calculation needs to be done.
|
||||
*
|
||||
* <p>The second utility is to help with the calculation of the memory size of a class, using the <a
|
||||
* href="https://github.com/openjdk/jol">JOL Tool</a>, to navigate the class layout and collect the
|
||||
* reported memory sizes.
|
||||
*
|
||||
* <p>For a correct calculation there are some things to consider, first exclude from the
|
||||
* calculation any reference to a <i>constant</i> object, for example if a field is always a
|
||||
* reference to {@code Optional.empty()} then just count the reference size, but not the size of the
|
||||
* Optional since it is always the same instance for every pending transaction.
|
||||
*
|
||||
* <p>To study the layout of a class it is usually useful to create a test method with one or more
|
||||
* instance of it, then programmatically save a heap dump using the {@link #dumpHeap(String,
|
||||
* boolean)} method, then analyze the heap dump with a tool to identify which are the dynamic and
|
||||
* constant fields, then use the JOL Tool to print the class layout and walk in the object tree,
|
||||
* then complete the writing of the test that will verify the current amount of memory used by that
|
||||
* class.
|
||||
*/
|
||||
@EnabledOnOs({OS.LINUX, OS.MAC})
|
||||
public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPoolTest {
|
||||
/**
|
||||
* Classes that represent constant instances, across all pending transaction types, and are
|
||||
* ignored during the calculation
|
||||
*/
|
||||
private static final Set<Class<?>> SHARED_CLASSES =
|
||||
Set.of(SignatureAlgorithm.class, TransactionType.class);
|
||||
|
||||
/**
|
||||
* Field that points to constant values, across all pending transaction types, and are ignored
|
||||
* during the calculation
|
||||
*/
|
||||
private static final Set<String> COMMON_CONSTANT_FIELD_PATHS =
|
||||
Set.of(".value.ctor", ".hashNoSignature", ".signature.encoded.delegate");
|
||||
|
||||
/**
|
||||
* Field that points to constant values, for EIP-1559 and EIP-4844 pending transactions, and are
|
||||
* ignored during the calculation
|
||||
*/
|
||||
private static final Set<String> EIP1559_EIP4844_CONSTANT_FIELD_PATHS =
|
||||
Sets.union(COMMON_CONSTANT_FIELD_PATHS, Set.of(".gasPrice"));
|
||||
|
||||
/**
|
||||
* Field that points to constant values, for Frontier and Access List pending transactions, and
|
||||
* are ignored during the calculation
|
||||
*/
|
||||
private static final Set<String> FRONTIER_ACCESS_LIST_CONSTANT_FIELD_PATHS =
|
||||
Sets.union(COMMON_CONSTANT_FIELD_PATHS, Set.of(".maxFeePerGas", ".maxPriorityFeePerGas"));
|
||||
|
||||
/** Field which value is dynamic and can only be calculated at runtime */
|
||||
private static final Set<String> VARIABLE_SIZE_PATHS =
|
||||
Set.of(".chainId", ".to", ".payload", ".maybeAccessList");
|
||||
Set.of(
|
||||
".chainId",
|
||||
".to",
|
||||
".payload",
|
||||
".maybeAccessList",
|
||||
".versionedHashes",
|
||||
".blobsWithCommitments",
|
||||
".maybeCodeDelegationList");
|
||||
|
||||
@Test
|
||||
public void toSize() {
|
||||
@@ -100,6 +171,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
+ ", "
|
||||
+ gpr.klass().toString()
|
||||
+ "]");
|
||||
System.out.println(ClassLayout.parseClass(gpr.klass()).toPrintable());
|
||||
};
|
||||
|
||||
GraphWalker gw = new GraphWalker(gv);
|
||||
@@ -108,7 +180,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
|
||||
System.out.println("Optional To size: " + size);
|
||||
|
||||
assertThat(size.sum()).isEqualTo(PendingTransaction.OPTIONAL_TO_MEMORY_SIZE);
|
||||
assertThat(size.sum()).isEqualTo(OPTIONAL_TO_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -133,7 +205,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
size.add(cl.instanceSize());
|
||||
System.out.println("Base payload size: " + size);
|
||||
|
||||
assertThat(size.sum()).isEqualTo(PendingTransaction.PAYLOAD_BASE_MEMORY_SIZE);
|
||||
assertThat(size.sum()).isEqualTo(PAYLOAD_SHALLOW_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -167,39 +239,37 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
|
||||
gw.walk(maybeChainId);
|
||||
|
||||
assertThat(size.sum()).isEqualTo(PendingTransaction.OPTIONAL_CHAIN_ID_MEMORY_SIZE);
|
||||
assertThat(size.sum()).isEqualTo(OPTIONAL_CHAIN_ID_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void kgzCommitmentsSize() {
|
||||
blobsWithCommitmentsFieldSize(
|
||||
t -> t.getBlobsWithCommitments().get().getKzgCommitments(),
|
||||
PendingTransaction.BASE_LIST_SIZE,
|
||||
PendingTransaction.KZG_COMMITMENT_OR_PROOF_SIZE);
|
||||
LIST_SHALLOW_SIZE,
|
||||
KZG_COMMITMENT_OR_PROOF_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void kgzProofsSize() {
|
||||
blobsWithCommitmentsFieldSize(
|
||||
t -> t.getBlobsWithCommitments().get().getKzgProofs(),
|
||||
PendingTransaction.BASE_LIST_SIZE,
|
||||
PendingTransaction.KZG_COMMITMENT_OR_PROOF_SIZE);
|
||||
LIST_SHALLOW_SIZE,
|
||||
KZG_COMMITMENT_OR_PROOF_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blobsSize() {
|
||||
blobsWithCommitmentsFieldSize(
|
||||
t -> t.getBlobsWithCommitments().get().getBlobs(),
|
||||
PendingTransaction.BASE_LIST_SIZE,
|
||||
PendingTransaction.BLOB_SIZE);
|
||||
t -> t.getBlobsWithCommitments().get().getBlobs(), LIST_SHALLOW_SIZE, BLOB_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void versionedHashesSize() {
|
||||
blobsWithCommitmentsFieldSize(
|
||||
t -> t.getBlobsWithCommitments().get().getVersionedHashes(),
|
||||
PendingTransaction.BASE_LIST_SIZE,
|
||||
PendingTransaction.VERSIONED_HASH_SIZE);
|
||||
LIST_SHALLOW_SIZE,
|
||||
VERSIONED_HASH_SIZE);
|
||||
}
|
||||
|
||||
private void blobsWithCommitmentsFieldSize(
|
||||
@@ -260,8 +330,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
System.out.println(rl.toPrintable());
|
||||
System.out.println("BlobQuad size:" + rl.instanceSize());
|
||||
|
||||
assertThat(cl.instanceSize() + rl.instanceSize())
|
||||
.isEqualTo(PendingTransaction.BLOBS_WITH_COMMITMENTS_SIZE);
|
||||
assertThat(cl.instanceSize() + rl.instanceSize()).isEqualTo(BLOBS_WITH_COMMITMENTS_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -287,7 +356,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
size.add(cl.instanceSize());
|
||||
System.out.println("PendingTransaction size: " + size);
|
||||
|
||||
assertThat(size.sum()).isEqualTo(PendingTransaction.PENDING_TRANSACTION_MEMORY_SIZE);
|
||||
assertThat(size.sum()).isEqualTo(PENDING_TRANSACTION_SHALLOW_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,15 +382,18 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
|
||||
final var optAL = txAccessList.getAccessList();
|
||||
|
||||
final ClassLayout cl1 = ClassLayout.parseInstance(optAL);
|
||||
System.out.println(cl1.toPrintable());
|
||||
System.out.println("Optional size: " + cl1.instanceSize());
|
||||
final ClassLayout optionalClassLayout = ClassLayout.parseInstance(optAL);
|
||||
System.out.println(optionalClassLayout.toPrintable());
|
||||
System.out.println("Optional size: " + optionalClassLayout.instanceSize());
|
||||
|
||||
final ClassLayout cl2 = ClassLayout.parseInstance(optAL.get());
|
||||
System.out.println(cl2.toPrintable());
|
||||
System.out.println("Optional + list size: " + cl2.instanceSize());
|
||||
final ClassLayout listClassLayout = ClassLayout.parseInstance(optAL.get());
|
||||
System.out.println(listClassLayout.toPrintable());
|
||||
System.out.println(
|
||||
"Optional + list size: "
|
||||
+ (optionalClassLayout.instanceSize() + listClassLayout.instanceSize()));
|
||||
|
||||
assertThat(cl2.instanceSize()).isEqualTo(PendingTransaction.OPTIONAL_ACCESS_LIST_MEMORY_SIZE);
|
||||
assertThat(optionalClassLayout.instanceSize() + listClassLayout.instanceSize())
|
||||
.isEqualTo(OPTIONAL_ACCESS_LIST_SHALLOW_SIZE);
|
||||
|
||||
final AccessListEntry ale = optAL.get().get(0);
|
||||
|
||||
@@ -329,92 +401,100 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
|
||||
System.out.println("AccessListEntry container size: " + aleSize);
|
||||
|
||||
assertThat(aleSize).isEqualTo(PendingTransaction.ACCESS_LIST_ENTRY_BASE_MEMORY_SIZE);
|
||||
assertThat(aleSize).isEqualTo(ACCESS_LIST_ENTRY_SHALLOW_SIZE);
|
||||
|
||||
final Bytes32 storageKey = ale.storageKeys().get(0);
|
||||
final ClassLayout cl4 = ClassLayout.parseInstance(storageKey);
|
||||
System.out.println(cl4.toPrintable());
|
||||
System.out.println("Single storage key size: " + cl4.instanceSize());
|
||||
|
||||
assertThat(cl4.instanceSize())
|
||||
.isEqualTo(PendingTransaction.ACCESS_LIST_STORAGE_KEY_MEMORY_SIZE);
|
||||
assertThat(cl4.instanceSize()).isEqualTo(ACCESS_LIST_STORAGE_KEY_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codeDelegationListSize() {
|
||||
System.setProperty("jol.magicFieldOffset", "true");
|
||||
|
||||
TransactionTestFixture preparedTx =
|
||||
prepareTransaction(TransactionType.DELEGATE_CODE, 0, Wei.of(500), Wei.ZERO, 0, 0);
|
||||
Transaction txDelegateCode = preparedTx.createTransaction(KEYS1);
|
||||
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
|
||||
txDelegateCode.writeTo(rlpOut);
|
||||
|
||||
txDelegateCode =
|
||||
Transaction.readFrom(new BytesValueRLPInput(rlpOut.encoded(), false)).detachedCopy();
|
||||
System.out.println(txDelegateCode.getSender());
|
||||
System.out.println(txDelegateCode.getHash());
|
||||
System.out.println(txDelegateCode.getSize());
|
||||
|
||||
final var optCD = txDelegateCode.getCodeDelegationList();
|
||||
|
||||
final ClassLayout optionalClassLayout = ClassLayout.parseInstance(optCD);
|
||||
System.out.println(optionalClassLayout.toPrintable());
|
||||
System.out.println("Optional size: " + optionalClassLayout.instanceSize());
|
||||
|
||||
final ClassLayout listClassLayout = ClassLayout.parseInstance(optCD.get());
|
||||
System.out.println(listClassLayout.toPrintable());
|
||||
System.out.println(
|
||||
"Optional + list size: "
|
||||
+ (optionalClassLayout.instanceSize() + listClassLayout.instanceSize()));
|
||||
|
||||
assertThat(optionalClassLayout.instanceSize() + listClassLayout.instanceSize())
|
||||
.isEqualTo(OPTIONAL_CODE_DELEGATION_LIST_SHALLOW_SIZE);
|
||||
|
||||
final CodeDelegation codeDelegation = optCD.get().get(0);
|
||||
|
||||
long cdSize = sizeOfField(codeDelegation, "storageKeys.elementData[");
|
||||
|
||||
System.out.println("CodeDelegation container size: " + cdSize);
|
||||
|
||||
assertThat(cdSize).isEqualTo(CODE_DELEGATION_ENTRY_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baseEIP1559AndEIP4844TransactionMemorySize() {
|
||||
System.setProperty("jol.magicFieldOffset", "true");
|
||||
Transaction txEip1559 = createEIP1559Transaction(1, KEYS1, 10);
|
||||
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
|
||||
txEip1559.writeTo(rlpOut);
|
||||
|
||||
txEip1559 =
|
||||
Transaction.readFrom(new BytesValueRLPInput(rlpOut.encoded(), false)).detachedCopy();
|
||||
System.out.println(txEip1559.getSender());
|
||||
System.out.println(txEip1559.getHash());
|
||||
System.out.println(txEip1559.getSize());
|
||||
|
||||
final ClassLayout cl = ClassLayout.parseInstance(txEip1559);
|
||||
System.out.println(cl.toPrintable());
|
||||
LongAdder eip1559size = new LongAdder();
|
||||
eip1559size.add(cl.instanceSize());
|
||||
System.out.println(eip1559size);
|
||||
|
||||
final SortedSet<FieldSize> fieldSizes = new TreeSet<>();
|
||||
GraphWalker gw = getGraphWalker(EIP1559_EIP4844_CONSTANT_FIELD_PATHS, fieldSizes);
|
||||
|
||||
gw.walk(txEip1559);
|
||||
|
||||
fieldSizes.forEach(
|
||||
fieldSize -> {
|
||||
eip1559size.add(fieldSize.size());
|
||||
System.out.println(
|
||||
"("
|
||||
+ eip1559size
|
||||
+ ")["
|
||||
+ fieldSize.size()
|
||||
+ ", "
|
||||
+ fieldSize.path()
|
||||
+ ", "
|
||||
+ fieldSize
|
||||
+ "]");
|
||||
});
|
||||
|
||||
System.out.println("Base EIP1559 size: " + eip1559size);
|
||||
assertThat(eip1559size.sum())
|
||||
.isEqualTo(PendingTransaction.EIP1559_AND_EIP4844_SHALLOW_MEMORY_SIZE);
|
||||
assertThat(baseTransactionMemorySize(txEip1559, EIP1559_EIP4844_CONSTANT_FIELD_PATHS))
|
||||
.isEqualTo(EIP1559_AND_EIP4844_SHALLOW_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baseFrontierAndAccessListTransactionMemorySize() {
|
||||
final Transaction txFrontier =
|
||||
createTransaction(TransactionType.FRONTIER, 1, Wei.of(500), 0, KEYS1);
|
||||
assertThat(baseTransactionMemorySize(txFrontier, FRONTIER_ACCESS_LIST_CONSTANT_FIELD_PATHS))
|
||||
.isEqualTo(FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE);
|
||||
}
|
||||
|
||||
private long baseTransactionMemorySize(final Transaction tx, final Set<String> constantFields) {
|
||||
System.setProperty("jol.magicFieldOffset", "true");
|
||||
Transaction txFrontier = createTransaction(TransactionType.FRONTIER, 1, Wei.of(500), 0, KEYS1);
|
||||
BytesValueRLPOutput rlpOut = new BytesValueRLPOutput();
|
||||
txFrontier.writeTo(rlpOut);
|
||||
tx.writeTo(rlpOut);
|
||||
|
||||
txFrontier =
|
||||
final var baseTx =
|
||||
Transaction.readFrom(new BytesValueRLPInput(rlpOut.encoded(), false)).detachedCopy();
|
||||
System.out.println(txFrontier.getSender());
|
||||
System.out.println(txFrontier.getHash());
|
||||
System.out.println(txFrontier.getSize());
|
||||
System.out.println(baseTx.getSender());
|
||||
System.out.println(baseTx.getHash());
|
||||
System.out.println(baseTx.getSize());
|
||||
|
||||
final ClassLayout cl = ClassLayout.parseInstance(txFrontier);
|
||||
final ClassLayout cl = ClassLayout.parseInstance(baseTx);
|
||||
System.out.println(cl.toPrintable());
|
||||
LongAdder frontierSize = new LongAdder();
|
||||
frontierSize.add(cl.instanceSize());
|
||||
System.out.println(frontierSize);
|
||||
LongAdder baseTxSize = new LongAdder();
|
||||
baseTxSize.add(cl.instanceSize());
|
||||
System.out.println(baseTxSize);
|
||||
|
||||
final SortedSet<FieldSize> fieldSizes = new TreeSet<>();
|
||||
|
||||
GraphWalker gw = getGraphWalker(FRONTIER_ACCESS_LIST_CONSTANT_FIELD_PATHS, fieldSizes);
|
||||
GraphWalker gw = getGraphWalker(constantFields, fieldSizes);
|
||||
|
||||
gw.walk(txFrontier);
|
||||
gw.walk(baseTx);
|
||||
|
||||
fieldSizes.forEach(
|
||||
fieldSize -> {
|
||||
frontierSize.add(fieldSize.size());
|
||||
baseTxSize.add(fieldSize.size());
|
||||
System.out.println(
|
||||
"("
|
||||
+ frontierSize
|
||||
+ baseTxSize
|
||||
+ ")["
|
||||
+ fieldSize.size()
|
||||
+ ", "
|
||||
@@ -423,10 +503,8 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
+ fieldSize
|
||||
+ "]");
|
||||
});
|
||||
|
||||
System.out.println("Base Frontier size: " + frontierSize);
|
||||
assertThat(frontierSize.sum())
|
||||
.isEqualTo(PendingTransaction.FRONTIER_AND_ACCESS_LIST_SHALLOW_MEMORY_SIZE);
|
||||
System.out.println("Base tx size: " + baseTxSize);
|
||||
return baseTxSize.sum();
|
||||
}
|
||||
|
||||
private GraphWalker getGraphWalker(
|
||||
@@ -478,6 +556,7 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
+ ", "
|
||||
+ gpr.klass().toString()
|
||||
+ "]");
|
||||
System.out.println(ClassLayout.parseClass(gpr.klass()).toPrintable());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -489,6 +568,24 @@ public class PendingTransactionEstimatedMemorySizeTest extends BaseTransactionPo
|
||||
return size.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method useful for producing a heap dump when calculating the memory size of a new
|
||||
* object. Note that the file is not overwritten, so you need to remove it to create a new heap
|
||||
* dump.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
record FieldSize(String path, Class<?> clazz, long size) implements Comparable<FieldSize> {
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user