Here is a more in-depth breakdown of the steps for providing threshold encrypted transactions in an L2.
# Steps - Threshold Encrypted TX
* **Step 1** - Keypers (sequencers or sub committee) run a DKG protocol to create public and private key pair for the threshold encryption/decryption
* **Step 2.1** - User uses public key to encrypt tx body & signs tx envelope with public header, private tx body
* **Step 2.2** - User creates a zk proof that they have enough funds to pay for transaction
* **Step 2.3** - User submits signed tx envelope and zk proof
* **Step 3** - Sequencers check tx signatures, and collect encrypted as well as plaintext transactions for block n. For encrypted txs they also verify zk proof.
* **Step 4** - Keypers publish the corresponding decryption key
* **Step 5** - Sequencers execute encrypted tx after decrypting in block n+1
**Note for liveness:** We can mitigate liveness failure of keypers by allowing sequencers to produce a block without a decryption key if there is no encrypted transaction scheduled for execution, or in case keypers go offline the chain would recover by ignoring encrypted transactions and produce blocks only with plaintext transactions. The block that includes the encrypted transactions would still be part of the chain, the transactions simply won’t be executed.
# ZK Proof - User Balance >= TX Gas
Here is an example of how a user could theoretically prove they can pay for an encrypted transaction and the verifier also can check canonical balance offchain before including the tx. This may not be the most optimal way, normally I work with cryptographers / programmers for the zk part or do extensive research, but in this case I wanted to try this problem on my own to illustrate my thinking process. Note this is a bit simplified / in pseudocode (ie deserialization and min gas for a tx, or block gas limit, isn’t included).
**Private Inputs**
* tx_plaintext (including gasLimit, gasPrice, nonce, value) // signed tx envelope with body unencrypted
* user_balance_merkle_proof // merkle proof of users balance
**Public Inputs**
* tx_encrypted // tx envelope with tx body encrypted
* state_root // current ethereum state
**Function For Proof**
```
function has_enough_gas( tx_plaintext, user_balance_merkle_proof, tx_encrypted, state_root ) {
/* Check encrypted version of tx is same as original version */
require( tx_encrypted == threshold_encrypt_tx( tx_plaintext ) );
/* Check nonce to prevent spamming of the mempool (DoS) */
prevNonce = get_prev_nonce( state_root, tx_plaintext['sender'] );
require( tx_plaintext['nonce'] == prevNonce + 1 );
/* Verify merkle path to the state root which proves the canonical user balance */
userBalance = verify_get_user_balance( state_root, user_balance_merkle_proof ); // Verify user balance merkle proof is valid and the signature is valid, then return userBalance or false
require( userBalance );
/* Make sure user has enough gas to pay tx when decrypted */
return( userBalance - tx_plaintext['value'] >= tx_plaintext['gasLimit'] * tx_plaintext['gasPrice'] );
}
```