diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java index 6bd1a2aa588..0eff53c4e11 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java @@ -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; @@ -43,6 +44,8 @@ public class BlockHeader extends SealableBlockHeader private final Supplier parsedExtraData; + private final Optional rawRlp; + public BlockHeader( final Hash parentHash, final Hash ommersHash, @@ -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 rawRlp) { super( parentHash, ommersHash, @@ -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) { @@ -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, @@ -242,7 +304,8 @@ public static BlockHeader readFrom( excessBlobGas, parentBeaconBlockRoot, requestsHash, - blockHeaderFunctions); + blockHeaderFunctions, + Optional.of(headerRlp.raw())); } @Override diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 8ada82c19f6..f5efe7d9284 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -126,6 +126,8 @@ public class Transaction private final Optional blobsWithCommitments; private final Optional> maybeCodeDelegationList; + private final Optional rawRlp; + public static Builder builder() { return new Builder(); } @@ -181,7 +183,8 @@ private Transaction( final Optional chainId, final Optional> versionedHashes, final Optional blobsWithCommitments, - final Optional> maybeCodeDelegationList) { + final Optional> maybeCodeDelegationList, + final Optional rawRlp) { if (!forCopy) { if (transactionType.requiresChainId()) { @@ -242,6 +245,7 @@ private Transaction( this.versionedHashes = versionedHashes; this.blobsWithCommitments = blobsWithCommitments; this.maybeCodeDelegationList = maybeCodeDelegationList; + this.rawRlp = rawRlp; } /** @@ -665,6 +669,10 @@ public final Wei getEffectiveGasPrice(final Optional baseFeePerGas) { return getEffectivePriorityFeePerGas(baseFeePerGas).addExact(baseFeePerGas.orElse(Wei.ZERO)); } + public Optional getRawRlp() { + return rawRlp; + } + @Override public TransactionType getType() { return this.transactionType; @@ -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; @@ -1194,6 +1203,7 @@ public static class Builder { protected List versionedHashes = null; private BlobsWithCommitments blobsWithCommitments; protected Optional> codeDelegationAuthorizations = Optional.empty(); + protected Bytes rawRlp = null; public Builder copiedFrom(final Transaction toCopy) { this.transactionType = toCopy.transactionType; @@ -1299,6 +1309,11 @@ public Builder versionedHashes(final List versionedHashes) { return this; } + public Builder rawRlp(final Bytes rawRlp) { + this.rawRlp = rawRlp; + return this; + } + public Builder guessType() { if (codeDelegationAuthorizations.isPresent()) { transactionType = TransactionType.DELEGATE_CODE; @@ -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) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/AccessListTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/AccessListTransactionDecoder.java index b5a06042406..9474b784d81 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/AccessListTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/AccessListTransactionDecoder.java @@ -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 = @@ -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; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java index 88d502f7a18..f658b5e4dd4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java @@ -39,21 +39,23 @@ private CodeDelegationTransactionDecoder() { } public static Transaction decode(final RLPInput input) { - input.enterList(); - final BigInteger chainId = input.readBigIntegerScalar(); + RLPInput transactionRlp = input.readAsRlp(); + transactionRlp.enterList(); + final BigInteger chainId = transactionRlp.readBigIntegerScalar(); final Transaction.Builder builder = Transaction.builder() .type(TransactionType.DELEGATE_CODE) .chainId(chainId) - .nonce(input.readLongScalar()) - .maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar())) - .maxFeePerGas(Wei.of(input.readUInt256Scalar())) - .gasLimit(input.readLongScalar()) - .to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) - .value(Wei.of(input.readUInt256Scalar())) - .payload(input.readBytes()) + .nonce(transactionRlp.readLongScalar()) + .maxPriorityFeePerGas(Wei.of(transactionRlp.readUInt256Scalar())) + .maxFeePerGas(Wei.of(transactionRlp.readUInt256Scalar())) + .gasLimit(transactionRlp.readLongScalar()) + .to(transactionRlp.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) + .value(Wei.of(transactionRlp.readUInt256Scalar())) + .payload(transactionRlp.readBytes()) + .rawRlp(transactionRlp.raw()) .accessList( - input.readList( + transactionRlp.readList( accessListEntryRLPInput -> { accessListEntryRLPInput.enterList(); final AccessListEntry accessListEntry = @@ -63,13 +65,14 @@ public static Transaction decode(final RLPInput input) { accessListEntryRLPInput.leaveList(); return accessListEntry; })) - .codeDelegations(input.readList(CodeDelegationTransactionDecoder::decodeInnerPayload)); + .codeDelegations( + transactionRlp.readList(CodeDelegationTransactionDecoder::decodeInnerPayload)); - final byte recId = (byte) input.readUnsignedByteScalar(); - final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger(); - final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger(); + final byte recId = (byte) transactionRlp.readUnsignedByteScalar(); + final BigInteger r = transactionRlp.readUInt256Scalar().toUnsignedBigInteger(); + final BigInteger s = transactionRlp.readUInt256Scalar().toUnsignedBigInteger(); - input.leaveList(); + transactionRlp.leaveList(); return builder.signature(SIGNATURE_ALGORITHM.get().createSignature(r, s, recId)).build(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/EIP1559TransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/EIP1559TransactionDecoder.java index b99f37468e5..ba8e81e7d2d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/EIP1559TransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/EIP1559TransactionDecoder.java @@ -37,21 +37,23 @@ private EIP1559TransactionDecoder() { } public static Transaction decode(final RLPInput input) { - input.enterList(); - final BigInteger chainId = input.readBigIntegerScalar(); + RLPInput transactionRlp = input.readAsRlp(); + transactionRlp.enterList(); + final BigInteger chainId = transactionRlp.readBigIntegerScalar(); final Transaction.Builder builder = Transaction.builder() .type(TransactionType.EIP1559) .chainId(chainId) - .nonce(input.readLongScalar()) - .maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar())) - .maxFeePerGas(Wei.of(input.readUInt256Scalar())) - .gasLimit(input.readLongScalar()) - .to(input.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) - .value(Wei.of(input.readUInt256Scalar())) - .payload(input.readBytes()) + .nonce(transactionRlp.readLongScalar()) + .maxPriorityFeePerGas(Wei.of(transactionRlp.readUInt256Scalar())) + .maxFeePerGas(Wei.of(transactionRlp.readUInt256Scalar())) + .gasLimit(transactionRlp.readLongScalar()) + .to(transactionRlp.readBytes(v -> v.isEmpty() ? null : Address.wrap(v))) + .value(Wei.of(transactionRlp.readUInt256Scalar())) + .payload(transactionRlp.readBytes()) + .rawRlp(transactionRlp.raw()) .accessList( - input.readList( + transactionRlp.readList( accessListEntryRLPInput -> { accessListEntryRLPInput.enterList(); final AccessListEntry accessListEntry = @@ -61,18 +63,18 @@ public static Transaction decode(final RLPInput input) { accessListEntryRLPInput.leaveList(); return accessListEntry; })); - final byte recId = (byte) input.readUnsignedByteScalar(); + final byte recId = (byte) transactionRlp.readUnsignedByteScalar(); final Transaction transaction = builder .signature( SIGNATURE_ALGORITHM .get() .createSignature( - input.readUInt256Scalar().toUnsignedBigInteger(), - input.readUInt256Scalar().toUnsignedBigInteger(), + transactionRlp.readUInt256Scalar().toUnsignedBigInteger(), + transactionRlp.readUInt256Scalar().toUnsignedBigInteger(), recId)) .build(); - input.leaveList(); + transactionRlp.leaveList(); return transaction; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/FrontierTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/FrontierTransactionDecoder.java index e218eb0ac4d..641caff3e0f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/FrontierTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/FrontierTransactionDecoder.java @@ -41,18 +41,20 @@ public class FrontierTransactionDecoder { Suppliers.memoize(SignatureAlgorithmFactory::getInstance); public static Transaction decode(final RLPInput input) { - input.enterList(); + RLPInput transactionRlp = input.readAsRlp(); + transactionRlp.enterList(); final Transaction.Builder builder = Transaction.builder() .type(TransactionType.FRONTIER) - .nonce(input.readLongScalar()) - .gasPrice(Wei.of(input.readUInt256Scalar())) - .gasLimit(input.readLongScalar()) - .to(input.readBytes(v -> v.size() == 0 ? null : Address.wrap(v))) - .value(Wei.of(input.readUInt256Scalar())) - .payload(input.readBytes()); + .nonce(transactionRlp.readLongScalar()) + .gasPrice(Wei.of(transactionRlp.readUInt256Scalar())) + .gasLimit(transactionRlp.readLongScalar()) + .to(transactionRlp.readBytes(v -> v.size() == 0 ? null : Address.wrap(v))) + .value(Wei.of(transactionRlp.readUInt256Scalar())) + .payload(transactionRlp.readBytes()) + .rawRlp(transactionRlp.raw()); - final BigInteger v = input.readBigIntegerScalar(); + final BigInteger v = transactionRlp.readBigIntegerScalar(); final byte recId; Optional chainId = Optional.empty(); if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) { @@ -64,11 +66,11 @@ public static Transaction decode(final RLPInput input) { throw new RuntimeException( String.format("An unsupported encoded `v` value of %s was found", v)); } - final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger(); - final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger(); + final BigInteger r = transactionRlp.readUInt256Scalar().toUnsignedBigInteger(); + final BigInteger s = transactionRlp.readUInt256Scalar().toUnsignedBigInteger(); final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, recId); - input.leaveList(); + transactionRlp.leaveList(); chainId.ifPresent(builder::chainId); return builder.signature(signature).build(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index a97f24cbd04..7d2b7b22d6e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -58,6 +58,7 @@ public static void encodeRLP( final RLPOutput rlpOutput, final EncodingContext encodingContext) { final TransactionType transactionType = getTransactionType(transaction); + Bytes opaqueBytes = encodeOpaqueBytes(transaction, encodingContext); encodeRLP(transactionType, opaqueBytes, rlpOutput); } @@ -94,8 +95,16 @@ public static Bytes encodeOpaqueBytes( } else { final Encoder encoder = getEncoder(transactionType, encodingContext); final BytesValueRLPOutput out = new BytesValueRLPOutput(); - out.writeByte(transaction.getType().getSerializedType()); - encoder.encode(transaction, out); + transaction + .getRawRlp() + .ifPresentOrElse( + (rawRlp) -> + out.writeRLPBytes( + Bytes.concatenate(Bytes.of(transactionType.getSerializedType()), rawRlp)), + () -> { + out.writeByte(transaction.getType().getSerializedType()); + encoder.encode(transaction, out); + }); return out.encoded(); } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java index 19f3e5bf7a5..ba0b5d8e6b0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionRLPDecoderTest.java @@ -19,7 +19,9 @@ import static org.hyperledger.besu.evm.account.Account.MAX_NONCE; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLPException; @@ -79,6 +81,20 @@ void shouldDecodeWithHighNonce() { assertThat(transaction.getNonce()).isEqualTo(MAX_NONCE - 1); } + @Test + void testForAccessListTransaction() { + BlockDataGenerator gen = new BlockDataGenerator(); + Transaction accessListTransaction = gen.transaction(TransactionType.ACCESS_LIST); + Bytes encodedBytes = + TransactionEncoder.encodeOpaqueBytes(accessListTransaction, EncodingContext.BLOCK_BODY); + Transaction decodedTransaction = + TransactionDecoder.decodeOpaqueBytes(encodedBytes, EncodingContext.BLOCK_BODY); + assertThat(accessListTransaction).isEqualTo(decodedTransaction); + Bytes reencodedBytes = + TransactionEncoder.encodeOpaqueBytes(decodedTransaction, EncodingContext.BLOCK_BODY); + assertThat(encodedBytes).isEqualTo(reencodedBytes); + } + private static Collection dataTransactionSize() { return Arrays.asList( new Object[][] { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java index 43ec525e4af..e2e0166789d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransaction.java @@ -421,8 +421,8 @@ public boolean hasPriority() { * class changes its structure. */ public interface MemorySize { - int FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE = 904; - int EIP1559_AND_EIP4844_SHALLOW_SIZE = 1016; + int FRONTIER_AND_ACCESS_LIST_SHALLOW_SIZE = 912; + int EIP1559_AND_EIP4844_SHALLOW_SIZE = 1024; int OPTIONAL_TO_SIZE = 112; int OPTIONAL_CHAIN_ID_SIZE = 80; int PAYLOAD_SHALLOW_SIZE = 32; diff --git a/ethereum/rlp/build.gradle b/ethereum/rlp/build.gradle index b95243e2ead..7756e1bafa9 100644 --- a/ethereum/rlp/build.gradle +++ b/ethereum/rlp/build.gradle @@ -36,7 +36,6 @@ dependencies { implementation 'io.tmio:tuweni-units' implementation 'com.google.guava:guava' - jmh project(':util') testImplementation project(':testutil') diff --git a/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/AbstractRLPInput.java b/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/AbstractRLPInput.java index 51a7f87e12e..5ca695ef582 100644 --- a/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/AbstractRLPInput.java +++ b/ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/AbstractRLPInput.java @@ -31,7 +31,6 @@ import org.apache.tuweni.units.bigints.UInt64; abstract class AbstractRLPInput implements RLPInput { - private static final String errorMessageSuffix = " (at bytes %d-%d: %s%s[%s]%s%s)"; private final boolean lenient; diff --git a/ethereum/rlp/src/test/java/org/hyperledger/besu/ethereum/rlp/RLPTest.java b/ethereum/rlp/src/test/java/org/hyperledger/besu/ethereum/rlp/RLPTest.java index 144e17c55ee..fb885233ccc 100644 --- a/ethereum/rlp/src/test/java/org/hyperledger/besu/ethereum/rlp/RLPTest.java +++ b/ethereum/rlp/src/test/java/org/hyperledger/besu/ethereum/rlp/RLPTest.java @@ -22,6 +22,7 @@ import java.util.Random; import org.apache.tuweni.bytes.Bytes; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; public class RLPTest { @@ -176,7 +177,7 @@ public void testValidateWithListEndingAtStartOfList() { // ["0x02"] // ] Bytes validRlp = Bytes.fromHexString("c4c101c102"); - RLP.validate(validRlp); + Assertions.assertThatCode(() -> RLP.validate(validRlp)).doesNotThrowAnyException(); } private static Bytes h(final String hex) {