# MEV BooTEE: Partial Block Building with TEEs ![simple image with et 5](https://hackmd.io/_uploads/SJgd0H9t6.jpg) ## :dart: General Goals :small_blue_diamond:(Main) Increase proposer's power: - [ ] Proposer can specify transactions to be included :small_blue_diamond:Reduce reliance on the relayer: - [ ] Remove trust assumptions for the relayer - [ ] Reduce the work performed by the relayer :small_blue_diamond:(Bonus) Improved block building guarantees - [ ] Build block inside the TEE ## :beginner: Overview There are three entities involved: 1. Builder - has an ordering of transactions that must be preserved 2. Proposer - has the right to propose a block 3. Relayer - holds block auctions and proxies Builder-Proposer communication to prevent either from cheating; requires trust from both Builder and Proposer ### The Role of the Relayer Currently, there is a powerful assumption that the Relayer is honest. We want to remove this assumption and *enforce stronger guarantees* by forcing the Relayer to behave honestly. One way to achieve this is by running the logic of the Relayer inside a TEE - the trustworthyness of the Relayer is now provable, instead of assumed. Running the Relayer logic inside a TEE ensures that: 1. The Builder's bundle is not unbundled and the transaction order is maintained. 2. The auction is fair and profitable for the Proposer. To this end, we designed various approaches that rely on a TEE to put together a block that contains a ToB (Top of Block) coming from a Builder, and an RoB (Rest of Block) coming from a Proposer: 1. The first approach is to replace the Relayer with a TEE-enabled system that allows Builders to bid for ToB. The ToB that wins the auction is combined with the RoB submitted by the Proposer to assemble a block. We call this new system the *[TEE Assembler](#-TEE-Assembler)*. 2. The second approach is to run the Assembler logic inside a TEE deployed at either the Proposer's or Builder's end. This TEE is called a *[TEE Aide](#-Embedded-Approaches)* and replaces the need to run an Assmbler as a separate entity. Since the TEE Aide can be deployed at either the Builder or the Proposer, we present two distinct solutions: *[Proposer Aide](#Proposer-Aide)* and *[Builder Aide](#Builder-Aide)*. 3. The third approach is to fully build the block inside a TEE using an RoB coming from the Proposer - *[full TEE partial block building](#-Full-TEE-Partial-Builder)*. ### Design Decisions There are a couple of things we considered while designing these approaches, the most important being: - Who runs the TEE? - This will greatly impact how many systems need to be enhanced with a TEE. - Who holds the auction? - What is the communication model: who initiates the connection and how many rounds are there? - What runs inside the TEE? - It is important to note that in all approaches, the RoB will only be sent to the TEE logic to prevent unbundling. The table below summarizes how each of these approaches tackles these questions. <!-- | | Assembler | Proposer Aide | Builder Aide | Full Builder | |-----------------------|:-----------------------------:|:----------------------------:|:-------------------------------:|:-------------------------------:| | Who runs the TEE? | 3rd party (Relayer) | Proposer | Builder | Builder | | What runs in the TEE? | auction, block assembly | auction, block assembly | bid computation, block assembly | block building, bid computation, block assembly | | Connection Model | n to 1 / 1 to 1 | n to 1 | 1 to n | 1 to n | --> <!-- | Approach | Who runs the TEE | What runs in the TEE | Connection Model | |---------------|------------------|-----------------------------------------------|----------------------------------------------------------------| | [Assembler](#-TEE-Assembler) | 3rd party (Relayer) | auction, bid computation, block assembly | n to 1 (Builders to Assembler), 1 to 1 (Proposer to Assembler) | | [Proposer Aide](#Proposer-Aide) | Proposer | auction, bid computation, block assembly | 1 to n (Proposer to Builders) | | [Builder Aide](#Builder-Aide) | Builder | bid computation, block assembly | 1 to n (Proposer to Builders) | | [Full Builder](#-Full-TEE-Partial-Builder) | Builder | ToB building, block assembly, bid computation | 1 to n (Proposer to Builders) | --> <!-- | Approach | Who runs the auction | Who runs the TEE | What runs in the TEE | Connection Model | |---------------|----------------------|---------------------|-----------------------------------------------|----------------------------------------------------------------| | [Assembler](#-TEE-Assembler) | 3rd party (Relayer) | 3rd party (Relayer) | auction, bid computation, block assembly | n to 1 (Builders to Assembler), 1 to 1 (Proposer to Assembler) | | [Proposer Aide](#Proposer-Aide) | Proposer | Proposer | auction, bid computation, block assembly | 1 to n (Proposer to Builders) | | [Builder Aide](#Builder-Aide) | Proposer | Builder | bid computation, block assembly | 1 to n (Proposer to Builders) | | [Full Builder](#-Full-TEE-Partial-Builder) | Proposer | Builder | ToB building, block assembly, bid computation | 1 to n (Proposer to Builders) | --> | | [Assembler](#-TEE-Assembler) | [Proposer Aide](#Proposer-Aide) | [Builder Aide](#Builder-Aide) | [Full Builder](#-Full-TEE-Partial-Builder) | |----------------------|----------------------------------------------------------------|------------------------------------------|---------------------------------|---------------------------------| | Who runs the auction | 3rd party (Relayer) | Proposer | Proposer | Proposer | | Who runs the TEE | 3rd party (Relayer) | Proposer | Builder | Builder | | What runs in the TEE | auction, bid computation, block assembly | auction, bid computation, block assembly | bid computation, block assembly | bid computation, block assembly | | Connection Model | n to 1 (Builders to Assembler), 1 to 1 (Proposer to Assembler) | 1 to n (Proposer to Builders) | 1 to n (Proposer to Builders) | 1 to n (Proposer to Builders) | <!-- | | [Assembler](#-TEE-Assembler) | [Proposer Aide](#Proposer-Aide) | [Builder Aide](#Builder-Aide) | [Full Builder](#-Full-TEE-Partial-Builder) | |----------------------|----------------------------------------------------------------|------------------------------------------|---------------------------------|---------------------------------| | Who runs the TEE | 3rd party (Relayer) | Proposer | Builder | Builder | | What runs in the TEE | auction, bid computation, block assembly | auction, bid computation, block assembly | bid computation, block assembly | bid computation, block assembly | | Connection Model | n to 1 (Builders to Assembler), 1 to 1 (Proposer to Assembler) | 1 to n (Proposer to Builders) | 1 to n (Proposer to Builders) | 1 to n (Proposer to Builders) | --> Apart from these choices, we also need to answer the following question: how computationaly intense should the work done inside the TEE be? This questions is related to how *bid computation* and *block assembly* are being performed. ### Default Computation **Bid Computation:** One of the reponsibilities of the Relayer is to ensure that the bid advertized by the Builder is correct. By default, the TEE will compute the value associated with each ToB submitted by Builders. **Block Assembly:** The job of the TEE is to put together a ToB and an RoB. By default, the TEE will ensure that the ToB and RoB do not include transactions that conflict with each other. ### Optimistic Computation **Bid Computation:** With optimistic computation, the TEE will collect collateral from the Builder and assume that advertized bids are legitimate. If a bid turns out to be untruthful, the TEE will slash the Builder and compensate the Proposer. **Block Assembly:** With optimistic computation, the TEE will ensure that the ToB and RoB do not contain the same transactions, but will not do any more verifications. I.e., if a transaction in the RoB conflicts with the ToB, it will be reverted. ## :wrench: TEE Assembler :::info Replace the Relayer with a TEE Assembler: the Assembler receives partial blocks (ToBs) from Builders, and a list of transactions (RoB) from the Proposer and will select the block with the highest bid. ::: ```mermaid sequenceDiagram Builder X--)TEE Assembler: submit partial block note over TEE Assembler: compute bid Proposer-)+TEE Assembler: get_block(transaction list) note over TEE Assembler: assemble block with highest bid TEE Assembler-)-Proposer: header(bid) Proposer-)TEE Assembler: signed header ``` 1. TEE Assembler receives partial blocks from multiple Builders. The bids are computed and ordered. 2. The Proposer submits a transaction list and asks for a block from the TEE Assembler. 3. TEE Assembler combines the ToBs and the RoB (this can be done in parallel) to find the block that is most profitable for the Proposer. 4. TEE Assembler returns the header of the block to the Proposer. 5. If the Proposer selects the block, it will send the signed header to the TEE Assembler. The **transaction list** submitted by the Proposer can contain transactions that MUST be included, and transactions that COULD be included. The TEE Assembler will ensure that MUST include transactions are in the block (either ToB or RoB), and COULD include transactions are included "best-effort". We can define more ways for the Proposer to specify how to include transactions in the future. :::danger Disadvantages: 1. There must be an altruistic thrid party (TEE Assembler) with even more duties compared to the Relayer. ::: #### Exposed APIs <center> ![Assembler](https://hackmd.io/_uploads/SyxIoSBFp.png) </center> For Builders: ``` - submit_tob(block_number, txns) -> id - retract_tob(id) -> bool ``` <!-- : the Assembler ensures that the block is legitimate and computes the bid; returns an ID to the Builder that can be used to retract the block --> <!-- : the Assembler removes the block associated with id from the auction (the Assembler will not allow a Builder to retract a block if the bid has already been relayed to the Proposer) --> For Proposers: ``` - get_highest_bid(block_number, inclusion_list) -> (bid, header) - commit_header(signed_header) -> bool ``` <!-- - get_highest_bid(block_number, inclusion_list) -> (bid, header): the Proposer requests a block header from the Assembler (the request is signed with the Proposer key to prove authenticity); the block should include as many transactions as possible from the inclusion list; the Assmebler returns the highest bid together with the associated block header - commit_header(signed_header) -> bool: Proposer signs the header and sends it to the Assembler; Assembler behaves similarly to per MEV-Boost --> ## :wrench: Embedded Approaches This section explores how we can embed the logic of the Relayer inside either the Builder or the Proposer. We refer to this logic as the TEE Aide. <!-- ## :wrench: Approaches ### Solving Relayer-related goals Running the relayer inside a TEE means the relayer no longer needs to be trusted - instead, the trustworthyness of the relayer is provable. If we employ an optimistic approach to running the relayer, the relayer will be "dumb" and this will make achieving the goal of increasing the proposer's leverage difficult. Therefore, if we want to reduce the burden of the relayer, we can integrate it with either the proposer or the builder and run its logic inside a TEE. In the rest of this document, we will refer to the Relayer, or the part that facilitates the communication between proposer and builder, as the TEE Aide. There are two approaches to building the block with a ToB coming from the builder, and an RoB coming from the proposer: - Builder sends its partial block to the proposer and the proposer adds its transactions (MEV-BooTEE style) - Proposer sends a list of transactions to the builder and the builder commits to include them (PEPC-TEE style) --> ### Proposer Aide :::info Proposer employs a TEE module that knows the private key of the Proposer and can sign on behalf of the Proposer. ::: The TEE-Aide holds an auction for the ToB selecting the Builder that submits the ToB with the highest bid. Below we describe the interaction between the selected Builder and the TEE Aide: ```mermaid sequenceDiagram box Proposer participant Proposer participant TEE-Aide end Proposer--)TEE-Aide: submit transaction list Builder 1-)TEE-Aide: submit ToB note over TEE-Aide: block assembly & bid validation Builder N-)TEE-Aide: submit ToB note over TEE-Aide: block assembly & bid validation note over TEE-Aide: select Builder 1's block TEE-Aide--)Builder 1: release block note over TEE-Aide: wait TEE-Aide-)Network: release block ``` 1. Proposer sends a list of transactions to the TEE Aide. 2. Builder sends ToB and bid to TEE Aide. 3. TEE Aide computes the bid of the ToB. 4. TEE Aide includes as many transactions from the list specified by Proposer. 5. TEE Aide releases the block to the Builder. 6. TEE Aide releases the block to the network. The TEE Aide will first release the block to the Builder to give the Builder the chance to propagate it to its preferred peers. The TEE Aide will release the block to its peers before it's too late in case the Builder does not propagate it. :::danger Disadvantages: 1. TEE Aide is controlled by the Proposer. A malicious proposer may try to get access to the ToB by mounting side-channel attacks, or may try to interfere with the timing of the TEE Aide to affect the release of the block. 2. TEE Aide will include transactions from the Proposer's list in a "best effort" manner, i.e., Proposer cannot require that transactions MUST be included. 3. All proposers who want to support this have to undergo modifications. 4. Since the auction is performed on the Proposer side, Builders have to directly connect to the Proposer, or the Proposer must ask all Builders to send their ToBs. The latter incurs another round of communication. ::: <!-- Variations: 1. [Include all transactions on the Proposer's list] When bidding, the Builder submits the partial block as well. The TEE Aide accepts only bids whose partial block does not conflict with the transactions that the Proposer wants included. 2. [Proposer initiates connection instead of the other way around] The Proposer has a registry with all the Builders. The Proposer then initiates a connection with each Builder and asks the Builder to submit a bid. 3. [Directly inform the Builder which transactions to include - this requires that the previous two variations are also employed] The Proposer connects to the Builder and submits a list of desired transactions. The Builder then submits a bid and a block, and the TEE Aide verifies that the list of transactions is part of the block. --> #### Exposed APIs <center> ![Proposer Aide](https://hackmd.io/_uploads/rk2ATBrYT.png) </center> For Proposer: ``` - submit_inclusion_list(inclusion_list) -> bool ``` <!-- - get_highest_bid() -> bid: returns the highest bid submitted so far - submit_inclusion_list(inclusion_list) -> bool: the Proposer submits a list of transactions to be included in the block (can be called multiple times with updated lists; the most recent list is considered) - release_block() -> bool: Proposer allows the TEE Aide to release the highest bid block to the network (the TEE Aide takes the highest bid block submitted by Builders, adds the most recent inclusion list and releases the block) --> For Builder: ``` - submit_tob(block_number, txns) -> id - retract_tob(id) -> bool ``` <!-- - submit_block(block_number, txns) -> id: the TEE Aide ensures that the block is legitimate and computes the bid; returns an ID to the Builder that can be used to retract the block - retract_block(id) -> bool: the TEE Aide removes the block associated with id from the auction (the TEE Aide will not allow a Builder to retract a block if the bid has already been relayed to the Proposer) --> ### Builder Aide :::info Builder employs a TEE module that the Proposer can connect to to ask for a bid or submit a list of transactions to include - the TEE module will includes as many transactions as possible from this list (such that the block remains valid). ::: ```mermaid sequenceDiagram box Builder participant Builder participant TEE-Aide end Builder-)TEE-Aide: submit ToB note over TEE-Aide: compute bid Proposer-)+TEE-Aide: get bid TEE-Aide-)-Proposer: bid note over Proposer: select bid Proposer-)TEE-Aide: send transaction list note over TEE-Aide: add possible transactions TEE-Aide-)Proposer: send header(bid, transaction list) note over Proposer: sign header Proposer-)TEE-Aide: send signed header ``` 1. Builder builds a ToB and sends it to the TEE Aide. 2. TEE Aide computes the bid. 3. Proposer asks all Builders their bids. 4. Proposer selects a bid and sends a list of transactions to be included. 5. TEE-Aide includes as many transactions and sends the header of the block to Proposer. 6. Proposer sends back the signed header and everything follows MEV-Boost from here onwards. :::danger Disadvantages: 1. TEE Aide is responsible for slashing if [optimistic computation](#Optimistic-Computation) is used. Since the TEE Aide resides on the Builder, it is possible for the Builder to cheat and disconnect before slashing occurs. 2. TEE Aide will include transactions from the Proposer's list in a "best effort" manner, i.e., Proposer cannot require that transactions MUST be included. 3. The block is built is the untrusted domain. ::: #### Exposed APIs <center> ![Builder Aide](https://hackmd.io/_uploads/ByseCrrKT.png) </center> For Proposer: ``` - get_bid(block_number) -> bid - get_header(block_number, inclusion_list) -> (bid, header) - confirm_header(signed_header) -> bool ``` <!-- - get_bid(block_number) -> bid: returns the bid of the Builder's block - get_header(block_number, inclusion_list) -> (bid, header): returns the header of the block containing the best ToB of the Builder + the valid transactions that were included from the inclusion list together with the bid associated with this block - confirm_header(signed_header) -> bool: Proposer signs over the header and sends it to the TEE Aide --> For Builder: ``` - submit_tob(block_number, txns) -> bool ``` <!-- - submit_block(block_number, txns) -> bool: the Builder submits a block to the TEE Aide; the TEE Aide ensures that the block is legitimate and computes the bid (this method can be called multiple time and the most recent block is saved) --> ## :wrench: Full TEE Partial Builder :::info Builder runs entirely inside of a TEE. Proposer communicates directly with Builder and is able to specify a list of transactions that MUST be included. ::: ```mermaid sequenceDiagram Proposer--)Builder: send transaction list note over Builder: verify transaction list loop until deadline note over Builder: build the best block end Proposer-)+Builder: get bid(transaction list) Builder-)-Proposer: bid note over Proposer: selects bid Proposer-)+Builder: get header(transaction list) Builder-)-Proposer: header Proposer-)Builder: signed header Builder-)Network: broadcast header note over Builder: wait for header propagation Builder-)Proposer: release block ``` 1. Proposer can at any time submit a transaction list that Builder MUST include (Builder will verify that executing these transactions results in a valid state) 2. Builder builds the best block, ensuring that Proposer's required transactions are included 3. Proposer requests a bid from Builder, specifying the transaction list 4. Builder responds with a bid 5. If Proposer wants to select this Builder, it will send a get header request (specifiyng the transaction list ensures that the Builder and Proposer agree on what transactions must be included) 6. Builder responds with the header 7. Proposer sends the signed header to the Builder 8. Builder broadcasts header to the network and, after a delay, releases the block to the Proposer :::danger Disadvantages: 1. TEE-related overheads could be prohibitive if they cause the Builders to build suboptimal blocks. ::: #### Exposed APIs <center> ![Full Builder](https://hackmd.io/_uploads/H1n-0SSKa.png) </center> For Proposer: ``` - submit_transaction_list(block_number, transaction_list) -> bool - get_bid(block_number, inclusion_list) -> (bid, header) - confirm_header(signed_header) -> bool ``` <!-- - submit_transaction_list(block_number, transaction_list) -> bool: Propsoer submits a list of transactions that MUST be included in the block (this method is called to give the builder a heads up about the transactions that the Proposer wants included; request is signed by the Proposer to prove authenticity) - get_bid(block_number, inclusion_list) -> (bid, header): returns the header of the block containing the best ToB of the Builder + the transactions specified in the inclusion list together with the bid associated with this block - confirm_header(signed_header) -> bool: Proposer signs over the header and sends it to the Builder --> No APIs are exposed for the Builder since the Builder is... the Builder! ## :feet: Brief TEE Overview TEEs are isolated enclaves protected by hardware, typically the CPU. The enclave is what we call the trusted world, while everything else (e.g., the host OS) is referred to as the untrusted world. The CPU employs strict protection mechanisms to safeguard the enclave data in order to prevent leakage and unauthorized access. These mechanisms ensure that the trusted world communicates with the untrusted world via strictly monitored interfaces, that data is encrypted before leaving the trusted world, and that all execution state belonging to the enclave is properly cleared when a context switch occurs between the trusted and untrusted world. These protection mechanisms are not free and can incur performance degradation. For example, the first generation of Intel SGX was shown to be significantly slower at running computational demanding workloads due to hardware limitations [1]. However, as SGX technology advanced and hardware limits were pushed further, the overall performance of workloads running on SGX improved [2]. Moreover, enclave technology is continuously evolving and new approaches and improvements are being explored. One such technology is AMD SEV, a VM-based TEE. SEV was shown to have good performance while running high-performance computing workloads [1], as well as database workloads [3]. VM-based TEEs are also being explored by AWS (Nitro Enclaves) and Intel (TDX). ### How will partial block building fare on TEEs? Context switching between the trusted and untrusted worlds is usually the main culprit in performance degradation. In our case, we estimate the number of context switches required to be dependent on whether we do [optimistic computation](#Optimistic-Computation), [default computation](#Default-Computation) or [full building](#-Full-TEE-Partial-Builder) inside the TEE: - **Optimistic Computation:** Context switches occur as a result of communication initiated or accepted by the TEE, e.g., when the TEE receives a ToB from a Builder, or an inclusion list from a Proposer; - **Default Computation:** In addition to the communication costs corresponding to optimistic computation, with default computation we have to explicitly verify that a block is valid and compute the bid; this incurs additional costs associated with fetching information about the state of the network and validating block transactions; - **Full Building:** Building a block is a repeated verification as various combinations of transactions are checked in order to find the highest value block. In other words, we expect the costs of building to be some order of magnitude higher than the ones associated with default computation (how big the order of magnitude is depends on how many combinations we have to test). We also expect the overheads to depend on what TEE technology we use and how we optimize certain operations to minimize the number of context switches. ## :bulb: References [1] Akram, Ayaz & Giannakou, Anna & Akella, Venkatesh & Lowe-Power, Jason & Peisert, Sean. (2021). Performance Analysis of Scientific Computing Workloads on General Purpose TEEs [2] Muhammad El-Hindi, Tobias Ziegler, Matthias Heinrich, Adrian Lutsch, Zheguang Zhao, and Carsten Binnig. (2022). Benchmarking the Second Generation of Intel SGX Hardware [3] Maliszewski, Kajetan & Quiané-Ruiz, Jorge-Arnulfo & Traub, Jonas & Markl, Volker. (2022). What Is the Price for Joining Securely? Benchmarking Equi-Joins in Trusted Execution Environments