Try   HackMD

SSZ transition implementation guide

M1: StableContainer

Spec: EIP-7495: SSZ StableContainer
Tests: ethereum/EIPs

For EL, StableContainer is required but can skip Variant and OneOf initially.

StableContainer[N] merkleizes same as regular Container, but the Merkle tree is padded up to nextpow2(N) members.

When serializing StableContainer[N], a Bitvector[N] is prefixed with 1/0 denoting presence/absence of fields, in the order that they are specified. Absent fields are subsequently skipped when encoding the container.

When parsing, first read the Bitvector[N] and then use it to read the present fields in order. Ensure that extra bits referring to unknown fields are set to 0, and that no data is left after reading the buffer.

Implementation notes: https://hackmd.io/Swl71tWeS7WP_f4D8abKHA

Goals:

  • Pass tests for StableContainer

Reference implementations:

M2: TransactionSignature

Spec: EIP-6493: SSZ Transaction Signature Scheme

  1. Split Transaction into two pieces, TransactionPayload (unsigned transaction) and TransactionSignature (v/r/s). This split is core to the SSZ representation. Reconstruct the RLP representation during serialization.
  2. If you have both a pre-EIP-1559 gasPrice and post-EIP-1559 max_fee_per_gas fields, get rid of the gasPrice one and store it in max_fee_per_gas instead. Pass the effective_gas_price out-of-bands or compute it on demand (needs base fee from the corresponding block).
  3. Switch transaction signatures from v/r/s to y_parity/r/s. Reconstruct v from chain_id + y_parity on demand.
  4. Implement the ecdsa_pack_signature and ecdsa_unpack_signature functions from EIP-6493, and store the packed version instead of y_parity/r/s. Reconstruct y_parity/r/s on demand.
  5. Extend TransactionSignature with from_address. Fill it in lazily when the sender is computed using ecrecover. Read from that from_address field instead of computing ecrecover in multiple locations.

Goals:

  • Have signatures in the format that's close to the SSZ representation. This is also the native format within crypto libraries.

Reference implementation:

M3: TransactionPayload

Spec: EIP-6493: SSZ Transaction Signature Scheme
Tests: ethereum/EIPs

  1. Implement the conversion routines from RLP > SSZ (Reference implementation).
  2. Implement the conversion routines from SSZ > RLP (Reference implementation)
  3. Implement the compute_sig_hash and compute_tx_hash functions (Reference implementation)
  4. Ensure that all hash computation goes through compute_sig_hash (unsigned TransactionPayload) and through compute_tx_hash (signed Transaction)
  5. Ensure that the devp2p protocol handlers support custom serialization / deserialization via the SSZ > RLP and RLP > SSZ conversion functions.

Goals:

  • Still be interoperable with unchanged clients; no semantic changes
  • Have all functionality to convert transactions between RLP <-> SSZ formats

M4: EL block building

Spec: EIP-6404: SSZ Transactions Root

  1. When constructing EL blocks, replace the transactions_root computation with one based on SSZ.
  2. When sending the payload to the CL, continue sending as RLP for now.

Goals:

  • Replace the MPT while still being compatible with a stock CL implementation (no engine API changes)

Stretch goal: Receipts

Spec: EIP-6493: SSZ Transaction Signature Scheme
Spec: EIP-6466: SSZ SSZ Receipts Root

The overall process is similar to transactions, the following notable changes exist:

  1. The receipt now contains gas_used instead of cumulative_gas_used. The SSZ <-> RLP converters have to be on lists of receipts instead of individual receipts.
  2. The receipts now contains the contract_address, so the transactions list must be available before receipts can be converted.

Stretch goal: Withdrawals

Spec: EIP-6465: SSZ SSZ Withdrawals Root

Similar to transactions / receipts, except no changes to data structures should be needed.

Stretch goal: JSON-RPC

  1. When sending transactions via JSON-RPC, add a root field that contains the hash_tree_root(transaction) next to the existing hash field.
  2. Add an eth_getTransactionByRoot endpoint that allows lookup by root instead of hash. Also allow lookup of receipt by tx root.
  3. Lateron, an additional field with an inclusion proof will be added to this endpoint that proofs that root is included in transactions_root.

Stretch goal: Engine API

  1. Change the transaction encoding for engine to be no longer opaque but instead encode the backing SSZ Transaction as JSON using its canonical encoding.
  2. This requires a CL that also supports EIP-6493. Before this stretch goal, a stock CL can be used.

Stretch goal: SSZ BasicTransaction / BlobTransaction

  • New EIP-2718 transaction type
  • When validating them, have to check that the received from_address matches the ecrecover value. The from_address is exchanged on the network.
  • Encodes in SSZ on the network, no RLP <-> SSZ conversion necessary anymore
  • Has the same hash and root value.