Skip to content

Commit

Permalink
7582: Add naive RLP caching for BlockHeader, Transaction, and Withdra…
Browse files Browse the repository at this point in the history
…wal (#7988)

* 7582: Add naive RLP caching for BlockHeader, Transaction, and Withdrawal

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: spotless

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Fix broken test after merge

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Back out withdrawal changes

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Fix up compile error after merge

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Apply naive RLP caching in transaction encoder/decoder classes

Signed-off-by: Matilda Clerke <[email protected]>

* 8053: Add RLPDecodingHelpers.Kind.EM
and handle empty lists in AbstractRLPInput

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Add trace logging to AbstractRLPInput

Signed-off-by: Matilda Clerke <[email protected]>

* Revert previous change, implement new fix

Signed-off-by: Matilda Clerke <[email protected]>

* Revert previous change, implement new fix

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Finish applying naive RLP caching to transaction encoder/decoder classes

Signed-off-by: Matilda Clerke <[email protected]>

* Remove unused LOG

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Make explicit the expectation that RLP.validate does not throw an exception in test

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: SPOTLESS

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Remove redundant curly braces

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Remove Optional from rawRlp in Transaction.Builder

Signed-off-by: Matilda Clerke <[email protected]>

* 7582: Make new BlockHeader constructor private

Signed-off-by: Matilda Clerke <[email protected]>

* Refactor tranaction encoder classes

Signed-off-by: Matilda Clerke <[email protected]>

* Remove logging changes from RLP module

Signed-off-by: Matilda Clerke <[email protected]>

* spotless

Signed-off-by: Matilda Clerke <[email protected]>

---------

Signed-off-by: Matilda Clerke <[email protected]>
Signed-off-by: Matilda-Clerke <[email protected]>
Co-authored-by: Stefan Pingel <[email protected]>
  • Loading branch information
Matilda-Clerke and pinges authored Feb 3, 2025
1 parent 8d360a3 commit 2412586
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hyperledger.besu.evm.log.LogsBloomFilter;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

import com.google.common.base.Suppliers;
Expand All @@ -43,6 +44,8 @@ public class BlockHeader extends SealableBlockHeader

private final Supplier<ParsedExtraData> parsedExtraData;

private final Optional<Bytes> rawRlp;

public BlockHeader(
final Hash parentHash,
final Hash ommersHash,
Expand All @@ -66,6 +69,56 @@ public BlockHeader(
final Bytes32 parentBeaconBlockRoot,
final Hash requestsHash,
final BlockHeaderFunctions blockHeaderFunctions) {
this(
parentHash,
ommersHash,
coinbase,
stateRoot,
transactionsRoot,
receiptsRoot,
logsBloom,
difficulty,
number,
gasLimit,
gasUsed,
timestamp,
extraData,
baseFee,
mixHashOrPrevRandao,
nonce,
withdrawalsRoot,
blobGasUsed,
excessBlobGas,
parentBeaconBlockRoot,
requestsHash,
blockHeaderFunctions,
Optional.empty());
}

private BlockHeader(
final Hash parentHash,
final Hash ommersHash,
final Address coinbase,
final Hash stateRoot,
final Hash transactionsRoot,
final Hash receiptsRoot,
final LogsBloomFilter logsBloom,
final Difficulty difficulty,
final long number,
final long gasLimit,
final long gasUsed,
final long timestamp,
final Bytes extraData,
final Wei baseFee,
final Bytes32 mixHashOrPrevRandao,
final long nonce,
final Hash withdrawalsRoot,
final Long blobGasUsed,
final BlobGas excessBlobGas,
final Bytes32 parentBeaconBlockRoot,
final Hash requestsHash,
final BlockHeaderFunctions blockHeaderFunctions,
final Optional<Bytes> rawRlp) {
super(
parentHash,
ommersHash,
Expand All @@ -90,6 +143,7 @@ public BlockHeader(
this.nonce = nonce;
this.hash = Suppliers.memoize(() -> blockHeaderFunctions.hash(this));
this.parsedExtraData = Suppliers.memoize(() -> blockHeaderFunctions.parseExtraData(this));
this.rawRlp = rawRlp;
}

public static boolean hasEmptyBlock(final BlockHeader blockHeader) {
Expand Down Expand Up @@ -154,72 +208,80 @@ public Hash getBlockHash() {
* @param out The RLP output to write to
*/
public void writeTo(final RLPOutput out) {
out.startList();
rawRlp.ifPresentOrElse(
out::writeRLPBytes,
() -> {
out.startList();

out.writeBytes(parentHash);
out.writeBytes(ommersHash);
out.writeBytes(coinbase);
out.writeBytes(stateRoot);
out.writeBytes(transactionsRoot);
out.writeBytes(receiptsRoot);
out.writeBytes(logsBloom);
out.writeUInt256Scalar(difficulty);
out.writeLongScalar(number);
out.writeLongScalar(gasLimit);
out.writeLongScalar(gasUsed);
out.writeLongScalar(timestamp);
out.writeBytes(extraData);
out.writeBytes(mixHashOrPrevRandao);
out.writeLong(nonce);
do {
if (baseFee == null) break;
out.writeUInt256Scalar(baseFee);
out.writeBytes(parentHash);
out.writeBytes(ommersHash);
out.writeBytes(coinbase);
out.writeBytes(stateRoot);
out.writeBytes(transactionsRoot);
out.writeBytes(receiptsRoot);
out.writeBytes(logsBloom);
out.writeUInt256Scalar(difficulty);
out.writeLongScalar(number);
out.writeLongScalar(gasLimit);
out.writeLongScalar(gasUsed);
out.writeLongScalar(timestamp);
out.writeBytes(extraData);
out.writeBytes(mixHashOrPrevRandao);
out.writeLong(nonce);
do {
if (baseFee == null) break;
out.writeUInt256Scalar(baseFee);

if (withdrawalsRoot == null) break;
out.writeBytes(withdrawalsRoot);
if (withdrawalsRoot == null) break;
out.writeBytes(withdrawalsRoot);

if (excessBlobGas == null || blobGasUsed == null) break;
out.writeLongScalar(blobGasUsed);
out.writeUInt64Scalar(excessBlobGas);
if (excessBlobGas == null || blobGasUsed == null) break;
out.writeLongScalar(blobGasUsed);
out.writeUInt64Scalar(excessBlobGas);

if (parentBeaconBlockRoot == null) break;
out.writeBytes(parentBeaconBlockRoot);
if (parentBeaconBlockRoot == null) break;
out.writeBytes(parentBeaconBlockRoot);

if (requestsHash == null) break;
out.writeBytes(requestsHash);
} while (false);
out.endList();
if (requestsHash == null) break;
out.writeBytes(requestsHash);
} while (false);
out.endList();
});
}

public static BlockHeader readFrom(
final RLPInput input, final BlockHeaderFunctions blockHeaderFunctions) {
input.enterList();
final Hash parentHash = Hash.wrap(input.readBytes32());
final Hash ommersHash = Hash.wrap(input.readBytes32());
final Address coinbase = Address.readFrom(input);
final Hash stateRoot = Hash.wrap(input.readBytes32());
final Hash transactionsRoot = Hash.wrap(input.readBytes32());
final Hash receiptsRoot = Hash.wrap(input.readBytes32());
final LogsBloomFilter logsBloom = LogsBloomFilter.readFrom(input);
final Difficulty difficulty = Difficulty.of(input.readUInt256Scalar());
final long number = input.readLongScalar();
final long gasLimit = input.readLongScalar();
final long gasUsed = input.readLongScalar();
final long timestamp = input.readLongScalar();
final Bytes extraData = input.readBytes();
final Bytes32 mixHashOrPrevRandao = input.readBytes32();
final long nonce = input.readLong();
final Wei baseFee = !input.isEndOfCurrentList() ? Wei.of(input.readUInt256Scalar()) : null;
final RLPInput headerRlp = input.readAsRlp();
headerRlp.enterList();
final Hash parentHash = Hash.wrap(headerRlp.readBytes32());
final Hash ommersHash = Hash.wrap(headerRlp.readBytes32());
final Address coinbase = Address.readFrom(headerRlp);
final Hash stateRoot = Hash.wrap(headerRlp.readBytes32());
final Hash transactionsRoot = Hash.wrap(headerRlp.readBytes32());
final Hash receiptsRoot = Hash.wrap(headerRlp.readBytes32());
final LogsBloomFilter logsBloom = LogsBloomFilter.readFrom(headerRlp);
final Difficulty difficulty = Difficulty.of(headerRlp.readUInt256Scalar());
final long number = headerRlp.readLongScalar();
final long gasLimit = headerRlp.readLongScalar();
final long gasUsed = headerRlp.readLongScalar();
final long timestamp = headerRlp.readLongScalar();
final Bytes extraData = headerRlp.readBytes();
final Bytes32 mixHashOrPrevRandao = headerRlp.readBytes32();
final long nonce = headerRlp.readLong();
final Wei baseFee =
!headerRlp.isEndOfCurrentList() ? Wei.of(headerRlp.readUInt256Scalar()) : null;
final Hash withdrawalHashRoot =
!(input.isEndOfCurrentList() || input.isZeroLengthString())
? Hash.wrap(input.readBytes32())
!(headerRlp.isEndOfCurrentList() || headerRlp.isZeroLengthString())
? Hash.wrap(headerRlp.readBytes32())
: null;
final Long blobGasUsed = !input.isEndOfCurrentList() ? input.readLongScalar() : null;
final Long blobGasUsed = !headerRlp.isEndOfCurrentList() ? headerRlp.readLongScalar() : null;
final BlobGas excessBlobGas =
!input.isEndOfCurrentList() ? BlobGas.of(input.readUInt64Scalar()) : null;
final Bytes32 parentBeaconBlockRoot = !input.isEndOfCurrentList() ? input.readBytes32() : null;
final Hash requestsHash = !input.isEndOfCurrentList() ? Hash.wrap(input.readBytes32()) : null;
input.leaveList();
!headerRlp.isEndOfCurrentList() ? BlobGas.of(headerRlp.readUInt64Scalar()) : null;
final Bytes32 parentBeaconBlockRoot =
!headerRlp.isEndOfCurrentList() ? headerRlp.readBytes32() : null;
final Hash requestsHash =
!headerRlp.isEndOfCurrentList() ? Hash.wrap(headerRlp.readBytes32()) : null;
headerRlp.leaveList();
return new BlockHeader(
parentHash,
ommersHash,
Expand All @@ -242,7 +304,8 @@ public static BlockHeader readFrom(
excessBlobGas,
parentBeaconBlockRoot,
requestsHash,
blockHeaderFunctions);
blockHeaderFunctions,
Optional.of(headerRlp.raw()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ public class Transaction
private final Optional<BlobsWithCommitments> blobsWithCommitments;
private final Optional<List<CodeDelegation>> maybeCodeDelegationList;

private final Optional<Bytes> rawRlp;

public static Builder builder() {
return new Builder();
}
Expand Down Expand Up @@ -181,7 +183,8 @@ private Transaction(
final Optional<BigInteger> chainId,
final Optional<List<VersionedHash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments,
final Optional<List<CodeDelegation>> maybeCodeDelegationList) {
final Optional<List<CodeDelegation>> maybeCodeDelegationList,
final Optional<Bytes> rawRlp) {

if (!forCopy) {
if (transactionType.requiresChainId()) {
Expand Down Expand Up @@ -242,6 +245,7 @@ private Transaction(
this.versionedHashes = versionedHashes;
this.blobsWithCommitments = blobsWithCommitments;
this.maybeCodeDelegationList = maybeCodeDelegationList;
this.rawRlp = rawRlp;
}

/**
Expand Down Expand Up @@ -665,6 +669,10 @@ public final Wei getEffectiveGasPrice(final Optional<Wei> baseFeePerGas) {
return getEffectivePriorityFeePerGas(baseFeePerGas).addExact(baseFeePerGas.orElse(Wei.ZERO));
}

public Optional<Bytes> getRawRlp() {
return rawRlp;
}

@Override
public TransactionType getType() {
return this.transactionType;
Expand Down Expand Up @@ -1116,7 +1124,8 @@ public Transaction detachedCopy() {
chainId,
detachedVersionedHashes,
detachedBlobsWithCommitments,
detachedCodeDelegationList);
detachedCodeDelegationList,
Optional.empty());

// copy also the computed fields, to avoid to recompute them
copiedTx.sender = this.sender;
Expand Down Expand Up @@ -1194,6 +1203,7 @@ public static class Builder {
protected List<VersionedHash> versionedHashes = null;
private BlobsWithCommitments blobsWithCommitments;
protected Optional<List<CodeDelegation>> codeDelegationAuthorizations = Optional.empty();
protected Bytes rawRlp = null;

public Builder copiedFrom(final Transaction toCopy) {
this.transactionType = toCopy.transactionType;
Expand Down Expand Up @@ -1299,6 +1309,11 @@ public Builder versionedHashes(final List<VersionedHash> versionedHashes) {
return this;
}

public Builder rawRlp(final Bytes rawRlp) {
this.rawRlp = rawRlp;
return this;
}

public Builder guessType() {
if (codeDelegationAuthorizations.isPresent()) {
transactionType = TransactionType.DELEGATE_CODE;
Expand Down Expand Up @@ -1338,7 +1353,8 @@ public Transaction build() {
chainId,
Optional.ofNullable(versionedHashes),
Optional.ofNullable(blobsWithCommitments),
codeDelegationAuthorizations);
codeDelegationAuthorizations,
Optional.ofNullable(rawRlp));
}

public Transaction signAndBuild(final KeyPair keys) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,23 @@ private AccessListTransactionDecoder() {
}

public static Transaction decode(final RLPInput rlpInput) {
rlpInput.enterList();
RLPInput transactionRlp = rlpInput.readAsRlp();
transactionRlp.enterList();
final Transaction.Builder preSignatureTransactionBuilder =
Transaction.builder()
.type(TransactionType.ACCESS_LIST)
.chainId(BigInteger.valueOf(rlpInput.readLongScalar()))
.nonce(rlpInput.readLongScalar())
.gasPrice(Wei.of(rlpInput.readUInt256Scalar()))
.gasLimit(rlpInput.readLongScalar())
.chainId(BigInteger.valueOf(transactionRlp.readLongScalar()))
.nonce(transactionRlp.readLongScalar())
.gasPrice(Wei.of(transactionRlp.readUInt256Scalar()))
.gasLimit(transactionRlp.readLongScalar())
.to(
rlpInput.readBytes(
transactionRlp.readBytes(
addressBytes -> addressBytes.isEmpty() ? null : Address.wrap(addressBytes)))
.value(Wei.of(rlpInput.readUInt256Scalar()))
.payload(rlpInput.readBytes())
.value(Wei.of(transactionRlp.readUInt256Scalar()))
.payload(transactionRlp.readBytes())
.rawRlp(transactionRlp.raw())
.accessList(
rlpInput.readList(
transactionRlp.readList(
accessListEntryRLPInput -> {
accessListEntryRLPInput.enterList();
final AccessListEntry accessListEntry =
Expand All @@ -61,18 +63,18 @@ public static Transaction decode(final RLPInput rlpInput) {
accessListEntryRLPInput.leaveList();
return accessListEntry;
}));
final byte recId = (byte) rlpInput.readUnsignedByteScalar();
final byte recId = (byte) transactionRlp.readUnsignedByteScalar();
final Transaction transaction =
preSignatureTransactionBuilder
.signature(
SIGNATURE_ALGORITHM
.get()
.createSignature(
rlpInput.readUInt256Scalar().toUnsignedBigInteger(),
rlpInput.readUInt256Scalar().toUnsignedBigInteger(),
transactionRlp.readUInt256Scalar().toUnsignedBigInteger(),
transactionRlp.readUInt256Scalar().toUnsignedBigInteger(),
recId))
.build();
rlpInput.leaveList();
transactionRlp.leaveList();
return transaction;
}
}
Loading

0 comments on commit 2412586

Please sign in to comment.