Tx Lifecycle
This page describes how Seismic transactions work from end-to-end
Key management
The network manages a its keys through the
seismic-enclave-servercrate. Reth (and summit) can make RPC calls to this server. One such call is to get the network keysEnclave can boot in either
--genesis-nodeOR--peers <ip>mode. The former generates its own root key and will share it with peers after they pass validation. The latter will loop through its peer IPs until it receives the root key from one of themEnclave 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-rethboots, it requests the derived keys from the enclave and keeps them in memoryWe 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 transactionClients 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 & 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 a four other parameters to tighten the security of encryption. These are:
a 12 byte
encryptionNoncerecentBlockHash, which must be within 100 blocks of the latest blockan
expiresAtBlocknumber, after which this transaction is invalidoptionally, a boolean
signedRead. If set, will only allow the transaction to be used for signed reads. Otherwise it will be rejected by the transaction pool validation
The 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 EIP712 typed data, which browser extension wallets can sign. There is no message version 1, but we reserved this for implementing seismic transactions viaeth_personalSignThese 6 seismic-specific fields are called
SeismicElements: client encryption pubkey, the 4 security fields above, message version) are combined with the EOA address (sender),chainId,nonce,toaddress, andvalue. The collection of all 11 of these fields are referred to as the Seismic transaction metadata (TxSeismicMetadata) inseismic-alloy-consensusTo 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 metadataThe 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 tx type of
74or0x4athey have these
SeismicElementsattached as well
Decryption
Transactions sent with type
0x4abut incomplete Seismic elements are rejected from the tx pool, as are transactions not marked as0x4abut do contain Seismic elementsNodes 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 exact way as their public analogues. The consequence of this brings up 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. And as a result, it's totally allowed to altar shielded state with vanilla Ethereum transactions. It's also allowed to do the reverse, by altaring public state with Seismic transactions
Shielded storage
Inside solidity, we've defined 2 new opcodes:
CLOADandCSTORE. These are the shielded analogues to SLOAD and SSTORE. We know to use CLOAD/CSTORE iff the underlying type is an s-typeImportantly, we maintain only 1 state tree, as you can see in
seismic-trie(a fork of alloy-trie). We modified the state tree by adding an extra boolean flag to leaves,is_private. This is wrapped inside the struct calledFlaggedStorage, which replaces StorageValue (a plainU256). We use this flag to validate CLOAD/CSTORE calls, and to know whether we can expose this piece of storage, via e.g.eth_getStorageAtWhen we see a CLOAD opcode inside
seismic-revm, 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 with 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
SeismicElementsand the encrypted calldata, Seismic transactions have the same fields as legacy transactions. In particular, it uses the same gas parameters:gasPriceandgasLimitSeismic transactions cannot be used to deploy bytecode via
Createcalls. Instead we only allow Seismic transactions to be sent to a specific address. We did this to make metadata validation easier, and also we can’t see a good reason to deploy encrypted bytecode
Last updated

