Tx Lifecycle

This page describes how Seismic transactions work from end-to-end.

Key management

  • The network manages its keys through the seismic-enclave-serverarrow-up-right crate. Reth (and Summit) can make RPC calls to this server. One such call is to get the network keys

  • Enclave can boot in either --genesis-node or --peers <ip> mode. The former generates its own root key and shares it with peers after they pass validation. The latter loops through its peer IPs until it receives the root key from one of them

  • Enclave then derives a few different keys from that root key. Most importantly, it derives the encryption secret key. This is the key used to decrypt Seismic transaction calldata

  • When seismic-retharrow-up-right boots, it requests the derived keys from the enclave and keeps them in memory

  • We exposed a new RPC method, seismic_getTeePublicKey. Its response is the public key of the aforementioned secret key that decrypts Seismic transactions. Calling this endpoint is the first step that a client takes to build a Seismic transaction

  • Clients also generate their own secret key, which can be either ephemeral (default) or long-lived, if they prefer to manage this themselves

  • Their secret key and the network public key combine to create an AES key, which is used to encrypt calldata

Encryption & Metadata

  • The client will include their encryption public key in the transaction, along with four other parameters to tighten the security of encryption. These are:

    • a 12-byte encryptionNonce

    • recentBlockHash, which must be within 100 blocks of the latest block

    • an expiresAtBlock number, after which this transaction is invalid

    • optionally, a boolean signedRead. If set, it only allows the transaction to be used for signed reads. Otherwise, it will be rejected by transaction pool validation

  • There is one other field in Seismic transaction metadata: an integer called messageVersion. This field is a hack to allow browser extension wallets (e.g. MetaMask) to support the Seismic transaction type. Message version 0, the default, means a standard Seismic transaction as described above. Message version 2 corresponds to a transaction sent as EIP-712 typed data, which browser extension wallets can sign. There is no message version 1, but we reserved this for implementing Seismic transactions via eth_personalSign

  • These six Seismic-specific fields are called SeismicElements: the client encryption public key, the four security fields above, and the message version. They are combined with the EOA address (sender), chainId, nonce, to address, and value. The full set of 11 fields is referred to as the Seismic transaction metadata (TxSeismicMetadata) in seismic-alloy-consensusarrow-up-right

  • To encrypt calldata, we use AES with AEAD. The AEAD is an RLP encoding of the TxSeismicMetadata. The nonce for this encryption is the same 12-byte encryption nonce as we included in the metadata

  • The encrypted calldata is then passed to a Seismic transaction in the data/input field. Seismic transactions work just like Legacy Ethereum transactions, except:

    • they have a tx type of 74 or 0x4a

    • they have these SeismicElements attached as well

Decryption

  • Transactions sent with type 0x4a but incomplete Seismic elements are rejected from the tx pool, as are transactions not marked as 0x4a but do contain Seismic elements

  • Nodes will decrypt Seismic transactions by:

    • decoding the transaction

    • recovering the signer

    • assembling the metadata

    • encoding it as AEAD via RLP

    • generating the AES key by combining the network's secret key with the transaction's encryption public key

  • If transaction decryption fails, the transaction is removed from the transaction pool

  • If it succeeds, it passes to the revm transaction environment as plaintext

  • Calls to get transaction information after they have landed on chain should return input as the encrypted calldata, and not the plaintext

  • A note on calldata encoding: s-types are encoded the same way as their public analogues. The consequence is an interesting artifact of the Seismic protocol (and maybe a security concern): Solidity has no clue whether functions are called with Seismic transactions or not. As a result, it's totally allowed to alter shielded state with vanilla Ethereum transactions. It's also allowed to do the reverse, by altering public state with Seismic transactions

Shielded storage

  • Inside Solidity, we've defined two new opcodes: CLOAD and CSTORE. These are the shielded analogues to SLOAD and SSTORE. We use CLOAD/CSTORE when the underlying type is an s-type.

  • Importantly, we maintain only one state tree, as you can see in seismic-triearrow-up-right (a fork of alloy-trie). We modified the state tree by adding an extra boolean flag, is_private, to leaves. This is wrapped inside the struct called FlaggedStorage, which replaces StorageValue (a plain U256). We use this flag to validate CLOAD/CSTORE calls, and to know whether we can expose this piece of storage, via e.g. eth_getStorageAt

  • When we see a CLOAD opcode inside seismic-revmarrow-up-right, we load the storage slot and validate that it either has is_private set to true or is an uninitialized slot. For CSTORE, we write the storage value with is_private = true. We throw errors when trying to CLOAD/CSTORE a FlaggedStorage that has is_private = false (“Invalid public storage access”). Similarly, we throw errors when trying to SLOAD/SSTORE on FlaggedStorage that has is_private = true (“Invalid private storage access”). These errors are thrown inside seismic-revm.

  • Unlike SSTORE/SLOAD, the gas cost of CLOAD/CSTORE does not change based on the length of the word stored. This is to prevent attackers from deducing information about shielded storage values

Notes

  • Outside of SeismicElements and the encrypted calldata, Seismic transactions have the same fields as legacy transactions. In particular, it uses the same gas parameters: gasPrice and gasLimit

  • Seismic transactions cannot be used to deploy bytecode via Create calls. Instead, we only allow Seismic transactions to be sent to a specific address. We did this to make metadata validation easier, and we can’t see a good reason to deploy encrypted bytecode

Last updated