Chain State Derivation
Last updated
Last updated
You put a properly-encoded Facet transaction in the calldata of an Ethereum transaction and send it to the Facet inbox address. What happens next? How does your transaction become part of the Facet chain?
This process is executed by two pieces of software working together: facet-node and facet-geth. This architecture follows the Consensus/Execution split on the Ethereum L1, with facet-node
acting as the consensus client and facet-geth
as the execution layer.
facet-geth
is a fork of Optimism's op-geth. facet-node
is modeled after op-node, though it isn't a fork.
On a high level:
facet-node
fetches Facet transactions from Ethereum L1 transactions.
facet-node
creates and sends proposed Facet blocks to facet-geth
.
facet-geth
executes transactions and maintains Facet chain state.
Let's explore this process in more detail with some visualizations.
facet-node
connects to an L1 Ethereum RPC server and monitors each L1 block for Ethereum transactions that contain valid Facet transactions.
For each L1 block, facet-node
calls eth_getBlockByNumber
(to get transaction calldata) and eth_getBlockReceipts
(to get logs and transaction status).
facet-node
examines each successful transaction whose "to" address is address(0xface7)
or event logs whose only topic is bytes32(uint256(0xface7))
and attempts to decode the payload into a Facet transaction object with the following fields:
chain_id
to
value
max_fee_per_gas
gas_limit
data
extra_data
(optional)
If the payload decodes correctly, facet-node verifies the chain_id
, and builds a deposit transaction:
Users communicate with facet-node
using Facet transactions. facet-node
communicates block payloads to facet-geth
using a different transaction called a Deposit Transaction.
Deposit transactions (type 0x7E
) were created by Optimism in op-geth where they are used to represent rollup transactions that do not go through the sequencer. Facet uses them with two modifications.
Deposit transactions copy the data of these fields directly from Facet transactions:
to
value
max_fee_per_gas
(not present in OP Deposit Transactions)
gas_limit
data
The extra_data
field is dropped at this stage. Deposit Transactions also add these fields:
source_hash
: a unique identifier for the transaction.
l1_tx_origin
: the EOA that initiated the L1 transaction. This is important for FCT mint logic and is not present in the OP version.
from
: the signer of the transaction in the case of a calldata-based Facet transaction or the emitting contract in the case of a log-originated Facet transaction. In the latter case this value is aliased according to Optimism's rules.
mint
: the amount of FCT op-geth should mint the user before processing their transaction. More below on how this works.
Deposit transactions and sent to op-geth as a Facet block using the engine API (engine_forkchoiceUpdated
etc).
However, some processing is required. First, in accordance with the OP protocol, the first transaction in every Facet block is an L1 tx attributes transaction. Second, and also in accordance with OP, Facet block times are 12 seconds, which means Facet blocks cannot be 1-1 with Ethereum L1 blocks as they can skip slots. In such a case facet-node inserts "filler blocks" to preserve the 12 second timing.
Once facet-geth receives a block payload from facet-node it can process the transaction. For the most part this happens exactly as it would on the L1 or any EVM rollup. This is what makes Facet EVM-compatible. However, there are a few exceptions:
It is not possible for facet-node to submit an invalid block. The usual "pre-check" errors that would make a block invalid, for example a transaction having a max fee below the base fee, instead just affect the individual transaction.
A block's gas limit is not validated against the total of the gas limits of its transactions. Instead, each transaction attempts to buy gas sequentially and if there is no more gas in a block for a given transaction to buy that transaction fails (and the next transaction attempts to buy gas).
There is no priority fee. All transactions pay the base fee. If a user doesn't have enough FCT to pay for their max_fee_per_gas
* gas limit their transaction doesn't fail, they just pay the base fee.
Transactions initiated by L1 contracts cannot increase the contract's FCT balance. Any minted FCT beyond what is required to pay for the transaction is transferred to the EOA who initiated the transaction on the L1.
The derivation process is deterministic and its only input is Ethereum data available from any Ethereum RPC. Anyone can run a Facet Node to compute or validate the state of the Facet chain at any time.