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

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2bf32b1
7582: Add naive RLP caching for BlockHeader, Transaction, and Withdrawal
Matilda-Clerke Dec 5, 2024
0d42988
Merge branch 'refs/heads/main' into 7582-avoid-unnecessary-rlp-encodi…
Matilda-Clerke Dec 5, 2024
eebbab3
7582: spotless
Matilda-Clerke Dec 5, 2024
585ed8b
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Dec 11, 2024
007bc3c
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Dec 11, 2024
2fb60f8
7582: Fix broken test after merge
Matilda-Clerke Dec 11, 2024
7738076
7582: Back out withdrawal changes
Matilda-Clerke Dec 12, 2024
f0b89b4
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Jan 7, 2025
a00bd94
7582: Fix up compile error after merge
Matilda-Clerke Jan 7, 2025
6c0ac42
7582: Apply naive RLP caching in transaction encoder/decoder classes
Matilda-Clerke Jan 8, 2025
981a1e7
8053: Add RLPDecodingHelpers.Kind.EM
Matilda-Clerke Jan 20, 2025
85b0d4a
7582: Add trace logging to AbstractRLPInput
Matilda-Clerke Jan 20, 2025
ca7a2d5
Merge branch 'refs/heads/fix-rlp-validate-to-handle-empty-list' into …
Matilda-Clerke Jan 20, 2025
a9121b3
Revert previous change, implement new fix
Matilda-Clerke Jan 21, 2025
e565288
Revert previous change, implement new fix
Matilda-Clerke Jan 21, 2025
3e68934
Merge branch 'refs/heads/fix-rlp-validate-to-handle-empty-list' into …
Matilda-Clerke Jan 21, 2025
cb03e27
7582: Finish applying naive RLP caching to transaction encoder/decode…
Matilda-Clerke Jan 21, 2025
3cddf48
Remove unused LOG
Matilda-Clerke Jan 21, 2025
08fe8dd
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Jan 21, 2025
5ba365b
7582: Make explicit the expectation that RLP.validate does not throw …
Matilda-Clerke Jan 21, 2025
90e60c2
7582: SPOTLESS
Matilda-Clerke Jan 21, 2025
1d090fd
7582: Remove redundant curly braces
Matilda-Clerke Jan 27, 2025
8b1c901
7582: Remove Optional from rawRlp in Transaction.Builder
Matilda-Clerke Jan 27, 2025
4f6645e
7582: Make new BlockHeader constructor private
Matilda-Clerke Jan 27, 2025
4f75e4e
Refactor tranaction encoder classes
Matilda-Clerke Jan 28, 2025
2987627
Remove logging changes from RLP module
Matilda-Clerke Jan 28, 2025
03ee34a
spotless
Matilda-Clerke Jan 28, 2025
772b40f
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Jan 29, 2025
e480234
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Jan 31, 2025
4da8920
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
Matilda-Clerke Feb 2, 2025
61f4d65
Merge branch 'main' into 7582-avoid-unnecessary-rlp-encoding-during-sync
pinges Feb 3, 2025
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
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