Abstract
This EIP defines a migration process of existing Merkle-Patricia Trie (MPT) commitments for receipts to Simple Serialize (SSZ).
Motivation
While the consensus ExecutionPayloadHeader
and the execution block header map to each other conceptually, they are encoded differently. This EIP aims to align the encoding of the receipts_root
, taking advantage of the more modern SSZ format. This brings several advantages:
Reducing complexity: Merkle-Patricia Tries (MPT) are hard to work with. Replacing them with SSZ leaves only the state trie in the legacy MPT format.
Better for smart contracts: The SSZ format is optimized for production and verification of merkle proofs. It allows proving specific fields of containers and allows chunked processing.
Better for light clients: Light clients with access to the consensus
ExecutionPayloadHeader
no longer need to implement Merkle-Patricia Trie or RLP libraries to work with receipts.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Consensus ExecutionPayload
changes
A new Receipt
SSZ container is introduced to represent receipts.
Name | SSZ equivalent |
---|---|
Topic | Bytes32 |
TransactionType | uint8 |
Name | Value | Notes |
---|---|---|
BYTES_PER_LOGS_BLOOM | uint64(2**8) (= 256) | Fixed constant |
MAX_TOPICS_PER_LOG | uint64(2**2) (= 4) | LOG0 through LOG4 opcodes allow 0-4 topics per log |
MAX_LOG_DATA_SIZE | uint64(2**24) (= 16,777,216) | Recommended devp2p soft limit for entire receipt: 2 MiB |
MAX_LOGS_PER_RECEIPT | uint64(2**20) (= 1,048,576) | Same scaling factor as MAX_TRANSACTIONS_PER_PAYLOAD |
class ReceiptLog(Container):
address: Address
topics: List[Topic, MAX_TOPICS_PER_LOG]
data: ByteVector[MAX_LOG_DATA_SIZE]
class Receipt(Container):
status: uint256 # EIP-658
cumulative_transaction_gas_used: uint64
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
logs: List[ReceiptLog, MAX_LOGS_PER_RECEIPT]
class TypedReceipt(Container):
tx_type: TransactionType
payload: Receipt
The consensus ExecutionPayload
's receipts_root
now refers to an SSZ Root
instead of an MPT Hash32
.
class ExecutionPayload(Container):
...
receipts_root: Root
...
To compute the new receipts_root
, the list of individual TypedReceipt
containers is represented as a SSZ List
.
payload.receipts_root = hash_tree_root(List[TypedReceipt, MAX_TRANSACTIONS_PER_PAYLOAD](
receipt_0,
receipt_1,
receipt_2,
...
))
Consensus ExecutionPayloadHeader
changes
The consensus ExecutionPayloadHeader
is updated for the new ExecutionPayload.receipts_root
definition.
class ExecutionPayloadHeader(Container):
...
receipts_root: Root
...
payload_header.receipts_root = payload.receipts_root
Execution block header changes
The execution block header's receipts-root
is updated to match the consensus ExecutionPayloadHeader.receipts_root
.
Helpers
These helpers use TransactionType
constants as defined in EIP-6404.
def encode_receipt(receipt: TypedReceipt) -> bytes:
schema = (
(big_endian_int, receipt.status),
(big_endian_int, receipt.cumulative_transaction_gas_used),
(Binary[256, 256], receipt.logs_bloom),
(List([Binary[20, 20], List([Binary[32, 32]]), Binary[0, uint64(2**24)]]), [
(log.address, log.topics, log.data) for log in receipt.logs
]),
)
sedes = List([schema for schema, _ in schema])
values = [value for _, value in schema]
encoded = rlp.encode(values, sedes)
if receipt.tx_type == TRANSACTION_TYPE_EIP4844:
return [0x05] + encoded
if receipt.tx_type == TRANSACTION_TYPE_EIP1559:
return [0x02] + encoded
if receipt.tx_type == TRANSACTION_TYPE_EIP2930:
return [0x01] + encoded
if receipt.tx_type == TRANSACTION_TYPE_LEGACY:
return encoded
assert False
def decode_receipt(encoded_receipt: bytes) -> Receipt:
eip2718_type = encoded_receipt[0]
class RLPReceipt(rlp.Serializable):
fields = (
('status', big_endian_int),
('cumulative_transaction_gas_used', big_endian_int),
('logs_bloom', Binary[256, 256]),
('logs', List([Binary[20, 20], List([Binary[32, 32]]), Binary[0, uint64(2**24)]])),
)
if eip2718_type == 0x05:
tx_type = TRANSACTION_TYPE_EIP4844
pre = RLPReceipt.deserialize(encoded_receipt[1:])
elif eip2718_type == 0x02:
tx_type = TRANSACTION_TYPE_EIP1559
pre = RLPReceipt.deserialize(encoded_receipt[1:])
elif eip2718_type == 0x01:
tx_type = TRANSACTION_TYPE_EIP2930
pre = RLPReceipt.deserialize(encoded_receipt[1:])
elif 0xc0 <= eip2718_type <= 0xfe:
tx_type = TRANSACTION_TYPE_LEGACY
pre = RLPReceipt.deserialize(encoded_receipt)
else:
assert False
return Receipt(
status=pre.status,
cumulative_transaction_gas_used=pre.cumulative_transaction_gas_used,
logs_bloom=pre.logs_bloom,
logs=[ReceiptLog(
address=log[0],
topics=log[1],
data=log[2],
) for log in pre.logs],
)
Rationale
This aligns the receipt representation with the EIP-6404 transaction representation.
What about ReceiptLog
data?
ReceiptLog
data is formatted according to the Ethereum contract ABI. Merkleizing log data according to its original structure would be more useful than merkleizing it as a ByteVector
. However, the data structure is determined by the log event signature, of which only the hash is known. As the hash preimages are erased from emitted EVM logs, it is not reliably possible to recover the original log event signature. Therefore, log data is provided as a ByteVector
for now, with the option for a future EIP to extend it.
Backwards Compatibility
Applications that solely rely on the TypedReceipt
RLP encoding but do not rely on the receipts_root
commitment in the block header can still be used through a re-encoding proxy.
Applications that rely on the replaced MPT receipts_root
in the block header can no longer find that information.
Receipts are tied to transactions, so are typically tied to either the transaction hash, or to a block hash + sequential transaction index. At time of writing, there is no JSON-RPC endpoint to obtain a receipts proof. It is not expected that major applications rely on the Merkle-Patricia Trie commitment for receipts.
Test Cases
TBD
Reference Implementation
TBD
Security Considerations
None
Copyright
Copyright and related rights waived via CC0.