Custom Backends
+Understanding the OP Stack STF
+The OP Stack state transition is comprised of two primary components:
+-
+
- The derivation pipeline (
kona-derive
) +-
+
- Responsible for deriving L2 chain state from the DA layer. +
+ - The execution engine (
kona-executor
) +-
+
- Responsible for the execution of transactions and state commitments. +
- Ensures correct application of derived L2 state. +
+
To prove the correctness of the state transition, Kona composes these two components:
+-
+
- It combines the derivation of the L2 chain with its execution in the same process. +
- It pulls in necessary data from sources to complete the STF, verifiably unrolling the input commitments along the way. +
kona-client
serves as an implementation of this process, capable of deriving and executing a single L2 block in a
+verifiable manner.
++📖 Why just a single block by default?
+On the OP Stack, we employ an interactive bisection game that narrows in on the disagreed upon block -> block state +transition before requiring a fault proof to be ran. Because of this, the default implementation only serves +to derive and execute the single block that the participants of the bisection game landed on.
+
Backend Traits
+Covered in the FPVM Backend section of the book, kona-client
ships with an implementation of
+kona-derive
and kona-executor
's data source traits which pull in data over the PreimageOracle ABI.
However, running kona-client
on top of a different verifiable environment, i.e. a zkVM or TEE, is also possible
+through custom implementations of these data source traits.
op-succinct
is an excellent example of both a custom backend and a custom
+program, implementing both kona-derive
and kona-executor
's data source traits backed by sp1_lib::io
+in order to:
-
+
- Execute
kona-client
verbatim, proving a single block's derivation and execution on SP-1.
+ - Derive and execute an entire Span Batch
+worth of L2 blocks, using
kona-derive
andkona-executor
.
+
This section of the book outlines how you can do the same for a different platform.
+Custom kona-derive
sources
+Before getting started, we need to create custom implementations of the following traits:
+Trait | Description |
---|---|
ChainProvider | The ChainProvider trait describes the minimal interface for fetching data from L1 during L2 chain derivation. |
L2ChainProvider | The ChainProvider trait describes the minimal interface for fetching data from the safe L2 chain during L2 chain derivation. |
BlobProvider | The BlobProvider trait describes an interface for fetching EIP-4844 blobs from the L1 consensus layer during L2 chain derivation. |
Once these are implemented, constructing the pipeline is as simple as passing in the data sources to the PipelineBuilder
. Keep in mind the requirements for validation of incoming data, depending on your platform. For example, programs
+targeting zkVMs must constrain that the incoming data is indeed valid, whereas fault proof programs can offload this validation to the on-chain implementation of the host.
let chain_provider = ...;
+let l2_chain_provider = ...;
+let blob_provider = ...;
+let l1_origin = ...;
+
+let cfg = Arc::new(RollupConfig::default());
+let attributes = StatefulAttributesBuilder::new(
+ cfg.clone(),
+ l2_chain_provider.clone(),
+ chain_provider.clone(),
+);
+let dap = EthereumDataSource::new(
+ chain_provider.clone(),
+ blob_provider,
+ cfg.as_ref()
+);
+
+// Construct a new derivation pipeline.
+let pipeline = PipelineBuilder::new()
+ .rollup_config(cfg)
+ .dap_source(dap)
+ .l2_chain_provider(l2_chain_provider)
+ .chain_provider(chain_provider)
+ .builder(attributes)
+ .origin(l1_origin)
+ .build();
+
+From here, a custom derivation driver is needed to produce the desired execution payload(s). An example of this for
+kona-client
can be found in the DerivationDriver.
kona-mpt
/ kona-executor
sources
+Before getting started, we need to create custom implementations of the following traits:
+Trait | Description |
---|---|
TrieDBFetcher | The TrieDBFetcher trait describes the interface for fetching trie node preimages and chain information while executing a payload on the L2 chain. |
TrieDBHinter | The TrieDBHinter trait describes the interface for requesting the host program to prepare trie proof preimages for the client's consumption. For targets with upfront witness generation, i.e. zkVMs, a no-op hinter is exported as NoopTrieDBHinter . |
Once we have those, the StatelessL2BlockExecutor
can be constructed like so:
+#![allow(unused)] +fn main() { +let cfg = RollupConfig::default(); +let provider = ...; +let hinter = ...; + +let executor = StatelessL2BlcokExecutor::builder(&cfg, provider, hinter) + .with_parent_header(...) + .build(); + +let header = executor.execute_payload(...).expect("Failed execution"); +}
Bringing it Together
+Once your custom backend traits for both kona-derive
and kona-executor
have been implemented,
+your final binary may look something like that of kona-client
's.
+Alternatively, if you're looking to prove a wider range of blocks, op-succinct
's range
program
+offers a good example of running the pipeline and executor across a string of contiguous blocks.