diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java index 79fb1a50149..e0da32c03ff 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/TransactionReceipt.java @@ -57,6 +57,7 @@ public class TransactionReceipt implements org.hyperledger.besu.plugin.data.Tran private final int status; private final TransactionReceiptType transactionReceiptType; private final Optional revertReason; + private final Bytes rlp; /** * Creates an instance of a state root-encoded transaction receipt. @@ -78,7 +79,8 @@ public TransactionReceipt( cumulativeGasUsed, logs, LogsBloomFilter.builder().insertLogs(logs).build(), - revertReason); + revertReason, + null); } private TransactionReceipt( @@ -87,7 +89,8 @@ private TransactionReceipt( final long cumulativeGasUsed, final List logs, final LogsBloomFilter bloomFilter, - final Optional revertReason) { + final Optional revertReason, + final Bytes rlp) { this( transactionType, stateRoot, @@ -95,7 +98,8 @@ private TransactionReceipt( cumulativeGasUsed, logs, bloomFilter, - revertReason); + revertReason, + rlp); } /** @@ -118,7 +122,8 @@ public TransactionReceipt( cumulativeGasUsed, logs, LogsBloomFilter.builder().insertLogs(logs).build(), - revertReason); + revertReason, + null); } public TransactionReceipt( @@ -128,7 +133,18 @@ public TransactionReceipt( final List logs, final LogsBloomFilter bloomFilter, final Optional revertReason) { - this(transactionType, null, status, cumulativeGasUsed, logs, bloomFilter, revertReason); + this(transactionType, null, status, cumulativeGasUsed, logs, bloomFilter, revertReason, null); + } + + public TransactionReceipt( + final TransactionType transactionType, + final int status, + final long cumulativeGasUsed, + final List logs, + final LogsBloomFilter bloomFilter, + final Optional revertReason, + final Bytes rlp) { + this(transactionType, null, status, cumulativeGasUsed, logs, bloomFilter, revertReason, rlp); } public TransactionReceipt( @@ -153,7 +169,8 @@ private TransactionReceipt( final long cumulativeGasUsed, final List logs, final LogsBloomFilter bloomFilter, - final Optional revertReason) { + final Optional revertReason, + final Bytes rlp) { this.transactionType = transactionType; this.stateRoot = stateRoot; this.cumulativeGasUsed = cumulativeGasUsed; @@ -163,6 +180,7 @@ private TransactionReceipt( this.transactionReceiptType = stateRoot == null ? TransactionReceiptType.STATUS : TransactionReceiptType.ROOT; this.revertReason = revertReason; + this.rlp = rlp; } /** @@ -190,6 +208,13 @@ void writeTo(final RLPOutput rlpOutput, final boolean withRevertReason, final bo public void writeToForReceiptTrie( final RLPOutput rlpOutput, final boolean withRevertReason, final boolean compacted) { + if (rlp != null && !compacted) { + // at this point we can ignore withRevertReason, because we would only have the rlp if the + // receipt was received via p2p, which means the receipt does not contain the revert reason. + rlpOutput.writeRaw(rlp); + return; + } + if (!transactionType.equals(TransactionType.FRONTIER)) { rlpOutput.writeIntScalar(transactionType.getSerializedType()); } @@ -233,6 +258,19 @@ public static TransactionReceipt readFrom(final RLPInput input) { */ public static TransactionReceipt readFrom( final RLPInput rlpInput, final boolean revertReasonAllowed) { + return readFrom(rlpInput, revertReasonAllowed, false); + } + + /** + * Creates a transaction receipt for the given RLP + * + * @param rlpInput the RLP-encoded transaction receipt + * @param revertReasonAllowed whether the rlp input is allowed to have a revert reason + * @param keepRlp whether to store the rlp with the receipt + * @return the transaction receipt + */ + public static TransactionReceipt readFrom( + final RLPInput rlpInput, final boolean revertReasonAllowed, final boolean keepRlp) { RLPInput input = rlpInput; TransactionType transactionType = TransactionType.FRONTIER; if (!rlpInput.nextIsList()) { @@ -249,13 +287,14 @@ public static TransactionReceipt readFrom( LogsBloomFilter bloomFilter = null; - final boolean hasLogs = !input.nextIsList() && input.nextSize() == LogsBloomFilter.BYTE_SIZE; - if (hasLogs) { + final boolean hasBloomFilter = + !input.nextIsList() && input.nextSize() == LogsBloomFilter.BYTE_SIZE; + if (hasBloomFilter) { // The logs below will populate the bloom filter upon construction. bloomFilter = LogsBloomFilter.readFrom(input); } // TODO consider validating that the logs and bloom filter match. - final boolean compacted = !hasLogs; + final boolean compacted = !hasBloomFilter; final List logs = input.readList(logInput -> Log.readFrom(logInput, compacted)); if (compacted) { bloomFilter = LogsBloomFilter.builder().insertLogs(logs).build(); @@ -271,18 +310,23 @@ public static TransactionReceipt readFrom( revertReason = Optional.of(input.readBytes()); } + Bytes rlpBytes = null; + if (keepRlp) { + rlpBytes = rlpInput.raw(); + } + // Status code-encoded transaction receipts have a single // byte for success (0x01) or failure (0x80). if (firstElement.raw().size() == 1) { final int status = firstElement.readIntScalar(); input.leaveList(); return new TransactionReceipt( - transactionType, status, cumulativeGas, logs, bloomFilter, revertReason); + transactionType, status, cumulativeGas, logs, bloomFilter, revertReason, rlpBytes); } else { final Hash stateRoot = Hash.wrap(firstElement.readBytes32()); input.leaveList(); return new TransactionReceipt( - transactionType, stateRoot, cumulativeGas, logs, bloomFilter, revertReason); + transactionType, stateRoot, cumulativeGas, logs, bloomFilter, revertReason, rlpBytes); } } @@ -353,6 +397,10 @@ public Optional getRevertReason() { return revertReason; } + public Optional getRlp() { + return Optional.ofNullable(rlp); + } + @Override public boolean equals(final Object obj) { if (obj == this) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BodyValidation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BodyValidation.java index 15e8c3c804f..7051ec51118 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BodyValidation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BodyValidation.java @@ -112,12 +112,15 @@ public static Hash receiptsRoot(final List receipts) { IntStream.range(0, receipts.size()) .forEach( - i -> - trie.put( - indexKey(i), - RLP.encode( - rlpOutput -> - receipts.get(i).writeToForReceiptTrie(rlpOutput, false, false)))); + i -> { + final TransactionReceipt receipt = receipts.get(i); + trie.put( + indexKey(i), + receipt.getRlp().isPresent() + ? receipt.getRlp().get() + : RLP.encode( + rlpOutput -> receipt.writeToForReceiptTrie(rlpOutput, false, false))); + }); return Hash.wrap(trie.getRootHash()); } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java index 5b52078b23e..8716c2b8849 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java @@ -104,7 +104,7 @@ protected Optional>> processResponse( } final ReceiptsMessage receiptsMessage = ReceiptsMessage.readFrom(message); - final List> receiptsByBlock = receiptsMessage.receipts(); + final List> receiptsByBlock = receiptsMessage.receipts(true); if (receiptsByBlock.isEmpty()) { return Optional.empty(); } else if (receiptsByBlock.size() > blockHeaders.size()) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/ReceiptsMessage.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/ReceiptsMessage.java index 7ca823cd8d7..beda51b60b2 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/ReceiptsMessage.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/ReceiptsMessage.java @@ -74,6 +74,10 @@ public int getCode() { } public List> receipts() { + return receipts(false); + } + + public List> receipts(final boolean keepRlp) { final RLPInput input = new BytesValueRLPInput(data, false); input.enterList(); final List> receipts = new ArrayList<>(); @@ -81,7 +85,7 @@ public List> receipts() { final int setSize = input.enterList(); final List receiptSet = new ArrayList<>(setSize); for (int i = 0; i < setSize; i++) { - receiptSet.add(TransactionReceipt.readFrom(input, false)); + receiptSet.add(TransactionReceipt.readFrom(input.readAsRlp(), false, keepRlp)); } input.leaveList(); receipts.add(receiptSet); 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..6558832a844 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 @@ -476,7 +476,7 @@ public int enterList() { * * @see #enterList() * @param skipCount true if the element count is not required. - * @return -1 if skipCount==true, otherwise, the number of item of the entered list. + * @return -1 if skipCount==true, otherwise, the number of items of the entered list. */ public int enterList(final boolean skipCount) { if (currentItem >= size) {