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 b7419b1572d..c5347808467 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 @@ -170,22 +170,33 @@ private TransactionReceipt( * @param out The RLP output to write to */ public void writeTo(final RLPOutput out) { - writeTo(out, false); + writeTo(out, false, false); } public void writeToWithRevertReason(final RLPOutput out) { - writeTo(out, true); + writeToWithRevertReason(out, false); } - private void writeTo(final RLPOutput rlpOutput, final boolean withRevertReason) { + public void writeToWithRevertReason(final RLPOutput out, final boolean isCompressed) { + writeTo(out, true, isCompressed); + } + + private void writeTo( + final RLPOutput rlpOutput, final boolean withRevertReason, final boolean isCompressed) { if (transactionType.equals(TransactionType.FRONTIER)) { - writeToForReceiptTrie(rlpOutput, withRevertReason); + writeToForReceiptTrie(rlpOutput, withRevertReason, isCompressed); } else { - rlpOutput.writeBytes(RLP.encode(out -> writeToForReceiptTrie(out, withRevertReason))); + rlpOutput.writeBytes( + RLP.encode(out -> writeToForReceiptTrie(out, withRevertReason, isCompressed))); } } public void writeToForReceiptTrie(final RLPOutput rlpOutput, final boolean withRevertReason) { + writeToForReceiptTrie(rlpOutput, withRevertReason, false); + } + + public void writeToForReceiptTrie( + final RLPOutput rlpOutput, final boolean withRevertReason, final boolean isCompressed) { if (!transactionType.equals(TransactionType.FRONTIER)) { rlpOutput.writeIntScalar(transactionType.getSerializedType()); } @@ -200,8 +211,10 @@ public void writeToForReceiptTrie(final RLPOutput rlpOutput, final boolean withR rlpOutput.writeLongScalar(status); } rlpOutput.writeLongScalar(cumulativeGasUsed); - rlpOutput.writeBytes(bloomFilter); - rlpOutput.writeList(logs, Log::writeTo); + if (!isCompressed) { + rlpOutput.writeBytes(bloomFilter); + } + rlpOutput.writeList(logs, (log, out) -> log.writeTo(out, isCompressed)); if (withRevertReason && revertReason.isPresent()) { rlpOutput.writeBytes(revertReason.get()); } @@ -218,15 +231,21 @@ public static TransactionReceipt readFrom(final RLPInput input) { return readFrom(input, true); } + 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 isCompressed is compressed or not * @return the transaction receipt */ public static TransactionReceipt readFrom( - final RLPInput rlpInput, final boolean revertReasonAllowed) { + final RLPInput rlpInput, final boolean revertReasonAllowed, final boolean isCompressed) { RLPInput input = rlpInput; TransactionType transactionType = TransactionType.FRONTIER; if (!rlpInput.nextIsList()) { @@ -242,8 +261,16 @@ public static TransactionReceipt readFrom( final long cumulativeGas = input.readLongScalar(); // The logs below will populate the bloom filter upon construction. // TODO consider validating that the logs and bloom filter match. - final LogsBloomFilter bloomFilter = LogsBloomFilter.readFrom(input); - final List logs = input.readList(Log::readFrom); + LogsBloomFilter bloomFilter = null; + if (!isCompressed) { + bloomFilter = LogsBloomFilter.readFrom(input); + } + final List logs = input.readList(in -> Log.readFrom(in, isCompressed)); + + if (bloomFilter == null) { + bloomFilter = LogsBloomFilter.builder().insertLogs(logs).build(); + } + final Optional revertReason; if (input.isEndOfCurrentList()) { revertReason = Optional.empty(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStoragePrefixedKeyBlockchainStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStoragePrefixedKeyBlockchainStorage.java index 9134ca91c01..324bf087cc9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStoragePrefixedKeyBlockchainStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueStoragePrefixedKeyBlockchainStorage.java @@ -129,7 +129,7 @@ public Updater updater() { } private List rlpDecodeTransactionReceipts(final Bytes bytes) { - return RLP.input(bytes).readList(TransactionReceipt::readFrom); + return RLP.input(bytes).readList(input -> TransactionReceipt.readFrom(input, true, true)); } private Hash bytesToHash(final Bytes bytes) { @@ -365,7 +365,12 @@ private void remove(final Bytes prefix, final Bytes key) { } private Bytes rlpEncode(final List receipts) { - return RLP.encode(o -> o.writeList(receipts, TransactionReceipt::writeToWithRevertReason)); + return RLP.encode( + o -> + o.writeList( + receipts, + (transactionReceipt, rlpOutput) -> + transactionReceipt.writeToWithRevertReason(rlpOutput, true))); } private void removeVariables() { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/log/Log.java b/evm/src/main/java/org/hyperledger/besu/evm/log/Log.java index 02b523445e7..4b7405a2c76 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/log/Log.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/log/Log.java @@ -26,6 +26,7 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.MutableBytes; /** * A log entry is a tuple of a logger’s address (the address of the contract that added the logs), a @@ -54,30 +55,54 @@ public Log( this.topics = ImmutableList.copyOf(topics); } - /** - * Writes the log entry to the provided RLP output. - * - * @param out the output in which to encode the log entry. - */ public void writeTo(final RLPOutput out) { + writeTo(out, false); + } + + public void writeTo(final RLPOutput out, final boolean isCompacted) { out.startList(); out.writeBytes(logger); out.writeList(topics, (topic, listOut) -> listOut.writeBytes(topic)); - out.writeBytes(data); + if (isCompacted) { + final Bytes shortData = data.trimLeadingZeros(); + final int zeroLeadDataSize = data.size() - shortData.size(); + out.writeInt(zeroLeadDataSize); + out.writeBytes(shortData); + } else { + out.writeBytes(data); + } out.endList(); } + public static Log readFrom(final RLPInput in) { + return readFrom(in, false); + } /** * Reads the log entry from the provided RLP input. * * @param in the input from which to decode the log entry. * @return the read log entry. */ - public static Log readFrom(final RLPInput in) { + public static Log readFrom(final RLPInput in, final boolean isCompacted) { in.enterList(); final Address logger = Address.wrap(in.readBytes()); final List topics = in.readList(listIn -> LogTopic.wrap(listIn.readBytes32())); - final Bytes data = in.readBytes(); + final Bytes data; + if (isCompacted) { + final int zeroLeadDataSize = in.readInt(); + if (in.nextIsNull()) { + data = MutableBytes.create(zeroLeadDataSize); + in.skipNext(); + } else { + final Bytes shortData = in.readBytes(); + MutableBytes unCompactedData = MutableBytes.create(zeroLeadDataSize + shortData.size()); + unCompactedData.set(zeroLeadDataSize, shortData); + data = unCompactedData; + } + } else { + data = in.readBytes(); + } + in.leaveList(); return new Log(logger, data, topics); }