LUCID: Encrypted mempool with distributed payload propagation

By Anders Elowsson

Thanks to Julian Ma, Benedikt Wagner and Justin Florentine for feedback and review. Thanks also to participants of the early January Berlin sessions where certain properties of the design were discussed: Julian, Thomas, Justin, Vitalik, Caspar, Marios, Ansgar and Francesco.

1. Overview

This post introduces LUCID, an encrypted mempool design that bridges the gap between includer and proposer, while adhering to existing Ethereum mechanisms such as ePBS, BALs, and auditable builder bids (ABBs). The design is general purpose and can be used for, e.g., trustless self-decryption by the sender, decryption by a trusted party, or in threshold designs. LUCID is an acronym capturing key features:

  • IL – The design relies on inclusion lists (ILs) enforced by the fork-choice, like FOCIL, but tentatively modified to grant includers more equitable inclusion rights as well as encryption capabilities for MEV protection. Includers thus act more like proposers. The ILs can be staggered to improve censorship resistance, facilitate decentralized inclusion preconfirmations, and reward includers without inducing networking inefficiencies.
  • U – Two IL design options are considered: a Uniform price auction over inclusion lists (UPIL), which ranks transactions based on the fee that senders offer for inclusion; Unconditional ILs (UILs), where the includers determine priority order for their own list.
  • C – The ILs can carry Ciphertexts in sealed transactions (STs) that are propagated in an encrypted mempool and can be bundled. The STs are committed to in the ABB.
  • D – The payload is propagated in a Distributed fashion and only includes references to IL transactions in the network representation. The consensus representation remains a self-contained execution payload, and includes ST tickets used for debiting ST senders as well as the decrypted STs.

Many of the components described in this post can be adopted separately or together. For example, it is possible to run only the encryption scheme without distributed payload propagation (DPP); the only downside is worse broadcast efficiency. The timeline for LUCID is presented below.

Section 2 introduces the reader to the problem that is addressed and Section 3 takes a closer look at the encryption design. Section 4 presents DPP and Section 5 discusses how reveal optionality is dealt with in the design. Section 6 finally offers a brief discussion, where Section 6.1 details how a minimum viable EIP could be structured. Appendix A completes the vision for DPP by proposing staggered inclusion lists (SIL), where includers gradually build the payload during the slot. Appendix B presents a tighter design where STs to be decrypted are specified in the payload as opposed to in the ABB. Appendix C finally shows one decryption scheme that decryptors could deploy.

Timeline

Figure 1 provides an overview of the LUCID mechanism.

Figure 1. LUCID timeline. Builders produce ABBs with commitments to sealed transactions. These ABBs are included in the beacon block to allow decryptors to release keys in a timely manner before the next slot starts. Transactions listed in ILs can be included in the payload by reference.

  • Before T_1T1 – Includers propagate ILs that, besides plaintext transactions (PTs), can incorporate STs. The STs are either included individually or in a bundle (dark blue background), and can be sourced from a public encrypted mempool. Each ST consists of a signed ST ticket used for charging the sender and binding to a decryptor as well as the ciphertext encrypted_tx also included in the ST. The encrypted_tx decrypts to a signed PT that can have a different from field than the ST, together with a ToB_fee_per_gas that will be used for ordering the PT top-of-block (ToB) once decrypted. Senders encrypt transactions to a decryptor in an open design following the decryptor’s off‑protocol instructions (and if applicable, using its public key), or can otherwise self‑decrypt trustlessly. Appendix A describes how the ILs could be staggered to achieve better coverage and Appendix C gives an example encryption design the decryptor could deploy.
  • T_1T1 – Attesters (purple) of slot nn freeze their view of propagated ILs as well as the decryption keys for the previous block.
  • After T_1T1 – Once builders are confident they have observed all relevant ILs and keys (those in the frozen view of most attesters), they cast ABBs (an expanded ExecutionPayloadBid) for the right to build the block (blue rectangles). These ABBs contain “ST-commitments” of the hash of the STs and ST bundles in the ILs. The ABB also flags observed decryption keys from the previous slot.
  • T_2T2 – At the start of slot nn, the proposer selects a winning ABB, which is at least as encompassing as its own view of required ILs and keys. It includes that ABB in the beacon block.
  • After T_2T2 – Upon observing the ABB, nodes begin requesting any missing ILs as well as ST bytes that are referenced by the ABB’s ST‑commitments. Senders and decryptors can also independently propagate those bytes now.
  • T_3T3 – Attesters of slot nn cast a vote on the current head of the chain. If the beacon block is missing or if the included ABB fails their audit due to left out ILs or keys from their frozen view, they indicate the preceding block that is the head of the chain in their view. If the ABB passes their audit, they optimistically attest to the block.
  • After T_3T3 (Payload release) – The builder releases the payload which also carries ST tickets. The first transactions (white rectangle) are decrypted STs, previously committed to in block n-1n1, ordered by their decrypted ToB fee per gas. Regular PTs from the current slot follow (black rectangle), ordered freely by the builder. The builder references the IL transactions by index into the IL instead of propagating them anew (using a separate list in the network representation).
  • After T_3T3 (Payload reconstruction) – Each client resolves the IL transaction references against its local cache of ILs as well as ST-commitments and assembles the full payload. It computes and verifies the payload root and proceeds to execute the payload. The STs selected by the included ABB for next-slot decryption are represented in the execution payload by a list of ST tickets, which are charged according to their full specified gas_limit.
  • After T_3T3 (Key release) – Each decryptor observes the ST-commitments in the ABB that was included in the beacon block. It confirms that the ABB has correct data for its own ST-commitment (pertaining, e.g., to its gas_obligation specifying how much gas it consumes), that the aggregate of all gas_obligation entries is within the allotted share of the next payload (ToB_gas_limit), and that the beacon block is attested to. It propagates the signed key(s) that reveal the STs that fit into the next block. The key(s) are flooded P2P and observed by attesters of the next block before their deadline at T_5T5. The ToB of payload n+1n+1 can then be constructed with the decrypted STs ordered ToB.
  • T_4T4 – The payload timeliness committee (PTC) votes for the timeliness of the payload. Given the distributed design, the ILs carrying transactions referenced in the payload or the committed full STs/ST-bundles must by this point also have reached the PTC member for the vote to indicate a timely payload.
  • T_4T4 or T_5T5 – The deadline for the released keys can be enforced either by a PTC bitfield vote on their timeliness, or by attesters of slot n+1n+1 freezing their view of the released keys and using view-merge. Attesters vote on the next ABB contingent on adherence to either the PTC bitfield vote or the frozen key view.
  • After T_5T5 – The process follows the same trajectory as after T_1T1 (for the previous slot). The decrypted STs from slot nn are added ToB, ordered by ToB fee and charged that fee. Given that the STs are decrypted before block nn is constructed, the design is fully compatible with BALs: the builder of block n+1n+1 executes and prepares the BAL as normal.

2. Introduction

A concern for decentralized blockchains such as Ethereum is the monopoly that the single proposer has over transaction inclusion in each block, which may lead to censorship and suboptimal user welfare. For this reason, designs with multiple concurrent proposers (MCP) have been proposed (1, 2, 3, 4). Years of research into inclusion lists (ILs) (e.g., 1, 2) in parallel led to the Fork-choice enforced inclusion lists (FOCIL) design. FOCIL has via EIP-7805 gained ground as the primary option for facilitating censorship resistance (CR) in Ethereum, allowing several includers to propagate ILs of transactions to be included in the next block. However, when the block is full, the proposer still has the right to censor transactions, even if they are willing to pay more than the transactions that actually are included. Furthermore, a large proportion of censorable transactions must be propagated privately to avoid MEV, and can therefore not rely on FOCIL for CR.

Various IL designs that give the proposer less leeway in controlling the content of the block have been proposed. Unconditional ILs (UILs) require all listed transactions to be included, if they meet regular inclusion criteria. EIP-8046 introduced a Uniform price auction over inclusion lists (UPIL), which ensures that the proposer no longer can ignore the ILs when the block is full. Transactions are ranked by the fee they offer to pay, and those willing to pay the most are included, each paying the same uniform intra-block base fee, which is burned. This provides strong CR and is beneficial for time-sensitive transactions and a multidimensional fee market. Still, UILs and UPIL do not prevent MEV because transactions must still be propagated openly.

To prevent MEV, transactions must remain hidden until they are committed to. Various designs have been proposed for encrypted mempools, such as threshold encryption potentially leveraging proposer commitments, commit–reveal schemes as in Sealed transactions, or a hybrid of the two. Sealed transactions may under ePBS leverage ABBs. A commit–reveal scheme under fork-choice enforcement, as in sealed transactions, can further facilitate a wide variety of out-of-protocol encryption schemes—a form of generalized sealed transactions. A design in this direction but with same-slot decryption has also been proposed recently as a research post and EIP. Another strategy is to rely on smart-account encrypted mempools, which has also been proposed as an EIP.

A Hayekian argument for decentralized block-building is that a broader set of participants jointly will have the knowledge required for making a good block. It can be noted that only by shielding individual participants’ contributions to that block from MEV and guaranteeing inclusion for viable transactions do we actually enable them to communicate that knowledge in a trustless fashion.

Inclusion lists generally introduce data redundancy: transactions are gossiped in the mempool, then via the IL, and finally gossiped again inside the execution payload. By instead treating the inclusion list gossip as a “pre-fill” step, the payload can be effectively distributed across the slot duration rather than burst-propagated at the end. Transmission of the same bytes twice (once in IL gossip, once in the payload envelope) within a single slot window is thus avoided. Reducing the size of the ExecutionPayloadEnvelope minimizes propagation latency, allowing for shorter slot times, bigger ILs, or bigger blocks. The design is compatible with payload chunking (1, 2), which would simply operate on already somewhat compressed payloads under DPP.

While DPP increases network efficiency, it does not resolve the inefficiency of several ILs propagating the same transactions. It is desirable to come up with an in-protocol solution that decreases the overlap, and this post proposes staggered inclusion lists (Appendix A) to this end, which also facilitate trustless preconfirmations. To limit the number of independent decryption keys that must be reconciled before execution, the STs can be bundled before being committed. This post proposes LUCID, which realizes generalized sealed transactions (extending on 1, 2) while upgrading includers to shield transactions from MEV and to have equal rights to blockspace, thus bridging the gap between includer and proposer.

3. Encryption design

The encryption design is first described using the UPIL mechanism, and the changes required for the UIL mechanism are presented in Section 3.10. Vanilla FOCIL (conditional ILs) can also be used under the general design. It can be emphasized that several of the features outlined in Sections 3-4 such as DPP, UPIL, and bundles are not critical for a baseline LUCID encrypted mempool, as furhter discussed in Section 6.1.

3.1 Sealed transactions

A sealed transaction (ST) consists of a signed ST ticket and a ciphertext encrypted_tx. The ST has the following fields:

  • from which signs the ST ticket and pays the fees,
  • regular fields nonce, gas_limit, max_fee_per_gas, max_priority_fee_per_gas, max_ranking_fee_per_gas,
  • decryptor_address of the entity responsible for decryption,
  • decryptor_fee that will be paid upon execution of the decrypted plaintext transaction, under the condition that from is funded.
  • reveal_commitment, where reveal_commitment = hash_tree_root(RevealCommitmentPreimage(ticket.from, ticket.nonce, plaintext_tx, ToB_fee_per_gas)) binds the revealed plaintext payload to a specific paid ticket,
  • ciphertext_hash, where ciphertext_hash = keccak256(encrypted_tx) binds the ciphertext bytes,
  • encrypted_tx, which decrypts to RevealedTransaction(plaintext_tx, ToB_fee_per_gas).

In the spirit of promoting a general solution, the baseline idea is that the protocol only specifies how clients parse and open the encrypted_tx, while leaving it up to decryptors to define (off‑protocol) which key‑escrow/hybrid encryption construction they use. Concretely, encrypted_tx is an envelope:

encrypted_tx = header_len:u16 || header || ct_demct_dem = nonce || aead_ciphertext

The header is opaque to the protocol and may contain any decryptor‑specific metadata needed to recover the DEM key (e.g., a hybrid public key encryption (HPKE)/KEM encapsulation, threshold ciphertext under a threshold decryptor key, etc.). Decryptors publish off‑protocol instructions describing how senders populate the header and how the DEM key k_dem is derived for a given ticket. At reveal time, the decryptor releases only this per‑ticket DEM keying material (k_dem) so that anyone can decrypt ct_dem and validate the reveal against reveal_commitment.

Decryptors should ensure the released k_dem is context‑bound to the paid ticket (e.g., by deriving it using a domain‑separated encoding of (chain_id, ticket.from, ticket.nonce) as KDF input (HPKE info if using HPKE) and/or AEAD associated data). This makes the revealed key ticket‑specific, so releasing the decryption material for one ticket does not help decrypt ciphertexts attached to other tickets even if header bytes are copied across tickets (any long‑term secret recovered from the header must never be revealed). Appendix C gives one example of such a construction (h/t Benedikt Wagner). For trustless self‑decryption, the sender can keep things simple (e.g., set header_len = 0 and choose a fresh per‑ticket DEM key directly).

class RevealedTransaction(Container):plaintext_tx: Transaction # Type 2 transactionToB_fee_per_gas: uint64class RevealCommitmentPreimage(Container):ticket_from: ExecutionAddressticket_nonce: uint64plaintext_tx: TransactionToB_fee_per_gas: uint64class STTicket(Container):from: ExecutionAddressnonce: uint64gas_limit: uint64max_fee_per_gas: uint64max_priority_fee_per_gas: uint64max_ranking_fee_per_gas: uint64decryptor_address: ExecutionAddressdecryptor_fee: uint64reveal_commitment: Bytes32ciphertext_hash: Bytes32signature: Bytes65 # Signature by `from` over the ticket fieldsclass SealedTransaction(Container):ticket: STTicketencrypted_tx: ByteList[MAX_ENCRYPTED_TX_BYTES]

The gas_limit must be sufficient to cover the calldata cost of the byte size of the encrypted_tx, is bounded by MAX_TRANSACTION_GAS_LIMIT, and will be charged in full—there is no refund upon observing actual gas usage after execution. The decrypted plaintext_tx inside RevealedTransaction is an EIP‑1559 (type 2) transaction with max_priority_fee_per_gas = 0, max_fee_per_gas = 0 (since the decrypted transaction has already been prepaid by the ST ticket), and gas_limit = ticket.gas_limit.

The ST ticket’s nonce reuses Ethereum’s normal account nonce and is consumed regardless of whether the ciphertext is later revealed, since STs are charged regardless of execution success. The ST sender signs the ST ticket. The signature is computed over a domain‑separated digest that includes chain_id (and fork/version) and the hash-tree-root of the ticket fields (excluding signature).

The plaintext payload inside encrypted_tx is separately signed by the plaintext sender as part of plaintext_tx. Nodes validate an ST by verifying the ticket signature and checking that ciphertext_hash matches the hash of encrypted_tx.

3.2 Bundles of sealed transactions

Sealed transactions are committed to in the ABB. Should the encrypted mempool become popular, the large number of separate commitments and decryption keys may put strain on the network, particularly as Ethereum scales. There are thus reasons to limit the number of commitments to STs in the ABB, while still facilitating high throughput. Therefore, STs can be bundled together and committed to jointly.

All transactions of a bundle must have the same decryptor_address and the bundle must be signed by the entity in control of the decryptor_address. It is possible to let bundles carry the full STs, or simply contain hashes with a signature over the hashes. This leaves it up to the includer to reconstruct the full ST bundle. It seems reasonable to require ILs to carry the full ST bytes, given that these must be available by the PTC deadline. If the IL referencing the bundle is unavailable upon being observed in the beacon block, the full bundle must be propagated P2P.

The ability to include non-bundled encrypted mempool transactions is still beneficial for three separate reasons:

  1. any ST can be executed if included in an IL, providing spam resistance to the public mempool,
  2. designs such as threshold decryption can run without a sequencer that bundles transactions ex-ante,
  3. users do not need to propagate bundles for single STs they wish to decrypt trustlessly.

3.3 ST-commitments

Sealed transactions are committed to in “ST-commitments” in the ABB, either individually, or as part of bundles sourced from the public encrypted mempool (or possibly provided out-of-protocol, potentially then leveraging designs similar to MEV boost). Inclusion lists provide an important inclusion path for ST-commitments—the builder must adhere to the commitments they propose. Each ST-commitment specifies four fields:

class STCommitment(Container):bundle: bool # False if ST, True if ST-bundlecommitment_root: Bytes32 # ticket_root if ST, bundle_root if bundledecrypt: Bitfield # indices to release keys to decrypt the txgas_obligation: uint64 # sum of gas_limit over entries with decrypt=1

An ST-commitment is identified via the commitment_root, which is either the SSZ hash-tree-root of the signed ticket ticket_root = hash_tree_root(STTicket) or of the signed bundle, bundle_root, which covers all ticket_root entries in the bundle. A possible scheduled_root over ticket_root entries with decrypt=1 is discussed in Section 6.3. Note that the ciphertext bytes are bound to the ticket via ciphertext_hash = keccak256(encrypted_tx), which is included in the signed ticket.

3.4 Allotments and ToB gas limit

Each IL has an allotment of ST-commitments, which can initially be set rather low; for example, each IL may commit MAX_COMMITS_PER_IL = 4 ST-commitments. It is possible to enable the builder to make commitments of its own, for example MAX_COMMITS_PER_IL ST-commitments as well. However, for design simplicity, it may be easiest to then simply give the builder its own IL. There is a max_bytes_per_inclusion_list allotment for each IL, just as in FOCIL. It varies with the gas limit so that includers are not disadvantaged relative to the proposer when the block’s gas_limit increases. It can be computed as max_bytes_per_inclusion_list = gas_limit/(IL_COMMITTEE_SIZE * GAS_PER_AGG_IL_BYTES). Setting GAS_PER_AGG_IL_BYTES = 2**9 gives an IL byte-size of 7.15 KiB at a gas limit of 60M when using IL_COMMITTEE_SIZE = 16 as in FOCIL. The ST-commitments still count against the max_bytes_per_inclusion_list.

Each block has a ToB_gas_limit for the decrypted transactions, set to some fraction of the overall gas_limit of the previous block, tentatively 1/4 * gas_limit. There is also a ToB_marginal_ranking_fee_per_gas, computed as the maximum of the marginal_ranking_fee_per_gas of the current block (as specified in EIP-8046) and the ranking_fee_per_gas of the highest-ranked ST excluded from the ToB_gas_limit allocation for the next block.

3.5 Auditable builder bids (ABBs)

The builder makes auditable builder bids (ABBs) for the right to build the payload. The ABB extends the ExecutionPayloadBid by including a ToB_marginal_ranking_fee_per_gas, ST-commitments and a bitfield on key adherence for ST-commitments of the previous block:

class ILCommitments(Container):IL_root: Bytes32commits: List[STCommitment, MAX_COMMITS_PER_IL]class ILKeyAdherence(Container):key_adherence: List[Boolean, MAX_COMMITS_PER_IL]class ABB(Container):... # existing fields of the ExecutionPayloadBidToB_marginal_ranking_fee_per_gas: uint64IL_data: List[ILCommitments, IL_COMMITTEE_SIZE]key_adherence: List[ILKeyAdherence, IL_COMMITTEE_SIZE] # one bitfield per previous IL in a list of bitfields

The builder deduplicates the STs of the ST-commitments using (ticket.from, ticket.nonce). There can only be one ST with the same (ticket.from, ticket.nonce) in the deduplicated set that should go into the payload, having a decrypt bit set to 1. All other STs with the same (ticket.from, ticket.nonce) must have their decrypt bit set to 0. Furthermore, STs that generate an invalid ticket-nonce sequence for ticket.from, or that are not chargeable at the start of ticket charging (Section 3.9), are also removed in the same way. A deterministic rule for determining which ST to retain during deduplication is applied, selecting the instance in the bundle with the most STs and tie-breaking by the IL’s committee_index.

If there is more than one IL commitment to a specific ST-bundle, the duplicate commitments set decrypt to the canonical zero encoding 0 (a single-bit 0) to save space, and only the ST-bundle from the IL with the highest committee_index remains. To avoid malleability, when decrypt has no set bits it must be encoded as exactly 0 (single-bit 0) and interpreted as an all-zero mask regardless of bundle length; other all-zero encodings are invalid.

For duplicate STs within bundles, all share the same decryptor_address, making deduplication strictly an accounting matter for the decryptor. Further note that the builder is not free to set decrypt bits as it pleases. The attesters, builders (and all other nodes) for slot n+1n+1 will review that the decrypt bits were set strictly to facilitate the deterministic rules (for, e.g., deduplication), without leaving out valid STs surfaced by an IL. If the payload fails their audit, the protocol enters the recovery process under invalid payload, as described in Section 4.5.

Deduplicated STs are finally included by setting decrypt = 1 if their ranking_fee_per_gas exceeds the ToB_marginal_ranking_fee_per_gas, tie-breaking by ticket_root. After determining which STs to include, the gas_obligation is set for each ST‑commitment as the sum of all gas_limit entries with decrypt = 1 in that commitment. The ToB_marginal_ranking_fee_per_gas can further be used for compressing the decrypt bitfield to only span the permissible range of transactions that have a sufficient ranking_fee_per_gas.

The commitment_root in the ABB allows the decryptor to identify it and release the decryption keys upon observing a timely beacon block, even before the payload is released. Each IL_root is included in the ABB to resolve ambiguity under equivocation, as further discussed in Section 4.3 that deals with DPP.

Two methods can be used to keep builders from having to propagate the full ABB for each bid. The builder could specify the delta relative to its previous bid for each new bid, including a hash for reference. It can be expected that ST-commitments are established well before the bid deadline, meaning that the delta in later bids only needs to cover changes to bid values and payload from late-arriving PTs. The other option is to institute a pull-scheme, where builder bids are lightweight, and the proposer pulls the ABB from the winning builder before publication.

3.6 Attestations

Attesters compare the ABB with the ILs they have observed. They verify that:

  • the aggregate of all gas_obligation entries is within the ToB_gas_limit,
  • every timely observed IL without equivocations is correctly specified in the ABB, given the announced ToB_marginal_ranking_fee_per_gas.

Inclusion lists in the ABB that attesters have not observed are not reviewed. Nodes request these ILs after observing them in the ABB and the PTC will eventually vote on their timeliness.

3.7 Key release

The decryptor observes the ToB_marginal_ranking_fee_per_gas and its stipulated gas_obligation in the ABB propagated with beacon block nn. It verifies that the ToB_marginal_ranking_fee_per_gas and decrypt bitfield indeed produces the specified gas_obligation for its own ST-commitment and that the aggregate of all gas_obligation entries is within the ToB_gas_limit. This is a requirement for releasing the decryption key(s). Specifically, if the payload then turns out to be invalid or untimely, the decryptor’s ST-commitment will always fit within the ToB_gas_limit. This means that they can still be included ToB in the next valid payload, as further discussed in Section 4.5.

If the IL responsible for listing the ST (or bundle) is not available, the decryptor will at this point (if not done earlier) broadcast the full ST bytes (or bundle body) whose ticket_root/bundle_root matches the commitment_root in the ABB, such that nodes can validate the released key(s) by the PTC deadline. When the decryptor (or anyone) provides the full SealedTransaction bytes for a committed ST, nodes check that hash_tree_root(st.ticket) == commitment_root and keccak256(st.encrypted_tx) == st.ticket.ciphertext_hash. When the decryptor is confident that the beacon block will become canonical (for example after observing attestations for it), it releases its key(s).

At key‑release time, the decryptor publishes the symmetric keying material as described in Section 3.1. Keys are released in a message that is signed by the decryptor_address and identifies the associated ST-commitment by committee_index and commit_index, further including the slot number and the beacon_block_root of the beacon block that carried the associated ABB. For bundles, keys are wrapped in a list of the same length as the number of decrypt bits set to 1, ordered the same way as in the committed bundle.

Key messages are flooded P2P. Nodes relay any well‑formed key message that targets a commitment present in the ABB identified by (beacon_block_root, slot) and is signed by the decryptor_address specified in the ST-commitment. A key is valid if it decrypts the committed ciphertext to RevealedTransaction(plaintext_tx, ToB_fee_per_gas) such that hash_tree_root(RevealCommitmentPreimage(ticket.from, ticket.nonce, plaintext_tx, ToB_fee_per_gas)) == ticket.reveal_commitment, and the decrypted plaintext_tx satisfies the required fixed fields (max_priority_fee_per_gas = 0, max_fee_per_gas = 0, and gas_limit = ticket.gas_limit). The decrypted plaintext sender and nonce are not required to match the ST sender’s from and nonce.

Keys targeting the same beacon_block_root, committee_index and commit_index that are not byte-identical are an equivocation. Equivocated keys are treated similarly to IL equivocation in FOCIL: nodes propagate the first two distinct signed key messages they have observed for the same target to signal equivocation, and the builder is free to ignore the STs that the decryptor equivocated on. Attesters accept such omission if they have observed the equivocation evidence by their attestation deadline.

3.8 PTC vote

The PTC votes on DA pertaining to the payload broadcast and blob data. To comply with the distributed propagation of the payload (Section 4), the PTC vote also pertains to the availability of all data needed to reconstruct the committed ExecutionPayload: the referenced plaintext transaction bytes (tx_reference) and the referenced ST‑commitments and the underlying ST/bundle bytes referenced by those commitments.

It is thus the builder’s responsibility to ensure that the ILs it registered in its ABB have arrived by the time the PTC votes, but the decryptors can also independently propagate the corresponding ST bytes if ILs are unavailable. Further considerations on this topic are discussed in Section 4.3. Upon reconstructing the payload with the aid of stored ILs, PTC members verify that the root of the payload is correct. They also confirm with their vote that the ABB was correctly specified, as can be observed in the payload.

Optionally, each PTC member also broadcasts a signed bitfield indicating which ST‑commitments scheduled for next‑slot decryption had a valid key message observed by the key deadline. The bitfield is indexed over the scheduling ABB’s ST‑commitments with a non‑zero decrypt mask, in canonical order by (committee_index, commit_index) (i.e., by scanning IL_data in increasing committee_index and commits[] in increasing commit_index). The reasons for using the PTC vote are discussed in Section 6.3.

The builder and slot n+1n+1 attesters take a decision on timeliness informed by the PTC vote. To merge views, attesters may freeze their view of PTC votes before the start of slot n+1n+1. The builder may then get to enforce its view on timeliness when a key gets between e.g. 45-55% of the vote (below 45%, attesters enforce untimely; above 55% attesters enforce timely, as long as they have observed the key themselves by the voting deadline). Optionally, the builder may have the ability to propagate a separate structure with its observed votes for merging views.

3.9 Payment and inclusion

The consensus representation of the execution payload is extended with an st_tickets list and a decrypted_transactions list (both described in Section 4.2). These are used for charging the STs.

Charging ST tickets

Each ST ticket in st_tickets debits ticket.gas_limit * (base_fee_per_gas + ToB_marginal_ranking_fee_per_gas) + ticket.decryptor_fee from ticket.from, where ToB_marginal_ranking_fee_per_gas is taken from the ABB whose ST‑commitments scheduled that ticket (i.e., the ABB that defines the pending set). There is no refund in block n+1n+1 based on the actual gas_used, and if the ST is not decrypted, only the decryptor_fee is refunded. A block is invalid if any ticket in st_tickets cannot be fully charged.

Nodes verify that the ToB_marginal_ranking_fee_per_gas (see Section 3.4) was set correctly by comparing it to the ranking_fee_per_gas of the highest ranked validly includable ST excluded from the ToB. Since STs are charged based on their publicly visible gas_limit rather than actual gas used, the fundability check at commitment time is static and does not depend on execution of other transactions. This avoids the circular dependency issues that necessitate conservative balance checks in UPIL’s ranking mechanism.

Including and charging decrypted STs

Nodes observe the release of the keys and decrypt the ST transactions locally. The builder of block n+1n+1 gathers all keys it can find and produces an ABB containing a bitfield with the valid keys it adheres to. Attesters require that the builder adheres to all timely valid keys, where timeliness is determined via view-merge or a PTC vote as previously outlined. The STs that cannot be decrypted with the released key are ignored.

Nodes validate each decrypted ToB transaction against the specific ST ticket that produced it (via the full ST and the corresponding key). The reveal is valid only if hash_tree_root(RevealCommitmentPreimage(ticket.from, ticket.nonce, plaintext_tx, ToB_fee_per_gas)) == ticket.reveal_commitment and the decrypted plaintext_tx satisfies the required fixed fields (max_priority_fee_per_gas = 0, max_fee_per_gas = 0, and gas_limit = ticket.gas_limit).

The first step of processing the decrypted_transactions is to refund the ticket.decryptor_fee for all STs that were not decrypted. The senders are identified as those with indices missing from ticket_index (Section 4.2). The decryptor_fee from decrypted_transactions is instead credited to the associated ticket.decryptor_address. The protocol finally deducts and burns the ToB fee ticket.gas_limit * ToB_fee_per_gas from ticket.from. If the ticket.from cannot cover this fee, the transaction is not included.

Users submitting multiple STs whose decrypted plaintext_tx have the same from and have sequential nonces should ensure ToB_fee_per_gas is nonce‑compatible, since decrypted transactions are ordered primarily by ToB_fee_per_gas while execution still respects per‑sender nonce order.

3.10 Unconditional IL version

For the unconditional IL version, a few modifications are made, which are here detailed. The idea is to pursue “Unconditional FOCIL”, adhering to many of the design principles of FOCIL, but letting each includer’s list be unconditional. Besides being a viable path to an encrypted mempool as in LUCID, it can also be helpful under a multidimensional fee market as in EIP-7999. Further details and analysis on Unconditional FOCIL besides what is outlined here will be presented separately.

UIL with overflow

The ToB_marginal_ranking_fee_per_gas is removed from the ABB. Each IL is allowed to consume at least ST_gas_min_per_IL of ToB gas, a number calculated as ST_gas_min_per_IL = ToB_gas_limit // IL_COMMITTEE_SIZE. The actual ToB gas the IL can consume depends on how much other ILs consume. The max_bytes_per_inclusion_list can be set such that ILs can fill up calldata up to the ST_gas_min_per_IL, or lower. The order of the ST-commitments, and secondarily, the order of STs within bundles, defines the IL-specified priority order. The builder increases the maximum gas that an IL can consume until the aggregate of all gas_obligation entries exceeds the ToB_gas_limit, and then reduces that maximum to remove the last added transaction. Thus, the (non-deduplicated) aggregate of the gas_obligation entries must be within the ToB_gas_limit, just as previously.

Attesters check that every timely observed ST-commitment has a correctly specified gas_obligation, given the maximum of all gas_obligation entries and the published decrypt bits. Each decryptor also verifies this for their own commitment. They release the key(s) for transactions with decrypt = 1, such that the aggregate of the ST gas limits equals gas_obligation.

UIL without overflow

Under the unconditional IL version without overflow, each IL is capped at ST_gas_limit_per_IL = ToB_gas_limit // IL_COMMITTEE_SIZE of ToB gas. Because this cap is fixed per IL, the gas_obligation field can be omitted from the ABB. Since includers still could equivocate, it seems reasonable to retain the commitment_root in the ABB.

UIL together with UPIL

Another viable design is to let the IL unconditionally include ST_gas_limit_per_IL = ToB_gas_limit // IL_COMMITTEE_SIZE, and let additional transactions instead be included according to UPIL. The overhang is then limited by max_bytes_per_inclusion_list.

4. Distributed payload propagation

The payload relies on references to pre‑gossiped transaction data and prior ST‑commitments, instead of duplicating transaction bytes in the payload broadcast. The consensus representation is extended with st_tickets and decrypted_transactions (Section 4.2), but the regular transactions list remains a list of full transaction bytes as in today’s execution payload. The network representation sent over P2P differs and is used as a transient optimization.

4.1 Network representation

A lightweight payload broadcast is introduced. Besides carrying full transaction bytes for transactions that are not sourced from inclusion lists, the DistributedExecutionPayloadEnvelope also includes a list of pointers to transactions that nodes are expected to already have (or be able to fetch) from canonical ILs. Each ILTransactionPointer also specifies the position of the IL transaction in the block’s final transactions list (the consensus representation).

class ILTransactionPointer(Container):committee_index: uint8 # 0..IL_COMMITTEE_SIZE-1tx_index: uint16 # index into the canonical IL body for this committee_indexposition: uint32 # index in the final full transactions list

The network representation of the payload does not refer to the STs. The ABB that the builder also produces is sufficient for deterministically reconstructing the ST-tickets and decrypted STs. Specifically, the ST-commitments in the scheduling ABB, with their decrypt bit specified by the builder, identify the ST-tickets that the payload includes and their order. Likewise, the key_adherence bits identify the decrypted STs that should be included, with their order determined by the ToB fee. Further nuances pertaining to recovery are discussed in Section 4.5.

Thus, concretely, the DistributedExecutionPayloadEnvelope mirrors the ExecutionPayload header fields (everything needed to recompute block_hash), but relies on pointers (ILTransactionPointer) for transactions sourced from ILs, and ABBs for reconstructing ST-tickets and decrypted STs.

4.2 Consensus representation

The consensus representation of the execution payload is extended with two additional lists, st_tickets and decrypted_transactions.

st_tickets

The st_tickets is a list of signed ST tickets that are charged at the start of processing the block and correspond to the set of ST commitments that are pending (i.e., have not yet been charged). The builder must include every ST ticket that it sets decrypt=1 for in the ST-commitment of its ABB. Under recovery (Section 4.5), the builder must only include ST tickets corresponding to the pending ST‑commitments in the relevant ancestor ABB with decrypt = 1 that remain after deterministic filtering (dedup, nonce feasibility, chargeability).

The st_tickets list has a canonical order: it is constructed by scanning the scheduling ABB’s IL_data in increasing committee_index, then scanning commits[] in increasing commit_index, and (for bundles) scanning entries in increasing tx_index, and including the ST ticket for each entry that remains after deterministic filtering.

decrypted_transactions

The decrypted_transactions is a list of decrypted ToB transactions executed ToB in the current block. Each entry contains (ticket_index, plaintext_tx, ToB_fee_per_gas), where ticket_index is an index into the st_tickets of the block that scheduled the transactions currently being decrypted (normally, the previous block). The ticket_index is included as a lightweight pointer when processing the block, and a block is invalid if the same ticket_index appears more than once in decrypted_transactions. Whenever two sets of st_tickets (Section 4.5) point to the same decrypted_transactions list, the ticket_index is numbered such that the later of the st_tickets are indexed starting at len(st_tickets).

The entry is valid only if hash_tree_root(RevealCommitmentPreimage(t.from, t.nonce, plaintext_tx, ToB_fee_per_gas)) == t.reveal_commitment for t = st_tickets[ticket_index]. The ToB ordering must be by decreasing ToB_fee_per_gas, tie-breaking by ticket_index, skipping entries that are not executable due to nonce order when reached.

Properties of the consensus representation

The regular transactions list remains a list of full transaction bytes as in today’s execution payload format. The beacon block includes a SignedExecutionPayloadBid that commits to the full payload, in particular via its block_hash as in EIP-7732. The pointer-based payload format is only used as an ephemeral networking optimization: nodes reconstruct the full ExecutionPayload locally, compute/verify the resulting block_hash against the committed header, and then the execution layer client executes and stores the full transaction data as usual. Syncing operates on full blocks; pointers never become part of the consensus object.

4.3 IL root commitment in the ABB to protect against equivocations

An includer could propagate multiple ILs around the time that the block is built. The builder may only observe one of these ILs before committing to a payload, and decide to include transactions from that IL. There is a risk that nodes on the network instead observe a different IL from the includer, introducing ambiguity about which IL the builder referenced. Therefore, the builder defines the unique “canonical” IL from each includer by including a 32-byte SSZ IL_root for each one in the ABB. Specifically, if the distributed payload references any transaction from committee member i via ILTransactionPointer(committee_index=i, ...), then the ABB included in the beacon block must include a non-empty IL_root for i.

If the builder has not observed an IL from an includer, it leaves that entire entry empty. It is free to include the root of observed ILs that it will not reference in its block, if it wishes to obfuscate the content of the payload before reveal, but can also omit to do so.

Nodes are instructed to always forward the canonical IL also under equivocation (together with one additional IL to indicate that equivocation). If nodes have already forwarded two ILs from the same includer and learn that a third IL is canonical, they also forward the third IL when receiving it. The builder re-seeds its canonical ILs after the publication of the bid if it observes an equivocation. It remains possible to penalize includers for equivocations, and seems more relevant to do so under DPP, given the increased importance of the DA of the ILs.

4.4 Payload reconstruction

The complexity is encapsulated within the CL client. The workflow is as follows:

  1. IL reception: The CL client receives ILs via P2P and stores them in a local cache keyed by IL_root.
  2. Payload reception: It receives the SignedDistributedExecutionPayloadEnvelope, verifies the builder signature, and checks that its (slot, beacon_block_root, builder_index, block_hash) matches the SignedExecutionPayloadBid committed in the beacon block.
  3. Resolution and reconstruction:
    • The client resolves all ILTransactionPointer entries by looking up the canonical IL_root for committee_index from the ABB in the beacon block, fetching that IL from its cache (requesting it if missing), and reading the full transaction at tx_index. It merges these transactions with the envelope’s full transactions by inserting each IL transaction at its unique position in the final list.
    • The client constructs st_tickets deterministically from the scheduling ABB (normally, the current slot’s ABB; under recovery, the uncharged ancestor ABB). It scans IL_datacommits[]->(bundle tx_index) in canonical order, applies the deterministic filtering rules (dedup, nonce feasibility, chargeability), fetches the corresponding SealedTransaction bytes, and extracts the ST ticket for each remaining entry.
    • The client constructs and orders the decrypted_transactions deterministically from the ST commitments whose decryptions are due for this block: in normal operation these are the parent block’s scheduled ST commitments; under recovery this may include commitments due from both the most recent full ancestor and the first empty block (Section 4.5). For each such commitment that has a valid released key message which the ABB adheres to, it decrypts to obtain RevealedTransaction(plaintext_tx, ToB_fee_per_gas), identifies the corresponding charged ticket t, and verifies: hash_tree_root(RevealCommitmentPreimage(t.from, t.nonce, plaintext_tx, ToB_fee_per_gas)) == t.reveal_commitment. It then creates a decrypted_transactions entry that includes the ticket_index of t in st_tickets. Finally, it orders by decreasing ToB_fee_per_gas, tie-breaking by ticket_index, and applies the per-transaction payability check in that order (dropping any entry that fails it).
  4. Verification: The client computes the resulting block_hash and verifies it against the SignedExecutionPayloadBid in the beacon block.
  5. Execution: The client passes the fully formed ExecutionPayload to the engine API (engine_newPayload).
  6. PTC vote: Nodes keep fetching missing ILs/ST bytes/keys and retry reconstruction as new data arrives. PTC members vote “timely” at T_4T4 only if they have fully reconstructed the payload and verified block_hash by then; otherwise they vote “not timely”.

4.5 Recovery under rejected payload

Senders have the right to get their decrypted transactions included in the next block if the ABB of the beacon block specified a correct gas_obligation entry for their ST-commitment, the aggregate of all gas_obligation entries is within the ToB_gas_limit, and their ST-commitment and decryption key(s) are available. However, payload nn may be rejected, by no fault of the sender or decryptor, after the decryption keys are released. In this scenario, the next payload must include both the decrypted STs that were scheduled for payload nn and the STs with keys released in slot nn, that were scheduled for n+1n+1.

Concretely, the decrypted STs of a block are the STs that:

  • have ST bytes and keys available,
  • were listed in a correctly specified ABB, where that ABB
    • is not the ABB of the current slot,
    • has not yet had its decrypted ST-commitments included onchain.

Thus, when a block is empty (no payload), the next payload must include the decrypted STs that the empty block should have included, as well as the (now decrypted) STs that the empty block committed to in the ABB. The recovery payload can then not commit to new STs (as suggested by Potuz here), but must instead indicate whether the ST commitments of the empty block are currently available (byte+keys), using the key_adherence bit. These bits also flag for the ST tickets, that must under recovery go into the same payload as the decrypted STs. If the block fails to include the decrypted STs such that the chain recovers, it must also be voted as empty, and the duty to recover again falls upon the next payload.

In the baseline specification, decrypted STs are allotted at most 1/4 of the gas limit. This ensures that when a payload is missed, the next one can include two sets of decrypted STs, which at most consume 1/2 of the block’s gas limit.

5. Reveal optionality in LUCID

A possible concern under a sealed transactions commit–reveal scheme is the reveal optionality. Some STs will be used to “backrun”, e.g., changes in CEX prices si

Source
Disclaimer: The content above is only the author's opinion which does not represent any position of Followin, and is not intended as, and shall not be understood or construed as, investment advice from Followin.
Like
52
Add to Favorites
12
Comments