diff --git a/README.md b/README.md index 1ac30bd56..165d15c5a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ must implement. In the Sovereign SDK, we define a rollup as the combination of t 1. A [State Transition Function](./rollup-interface/specs/interfaces/stf.md) ("STF") which defines the "business logic" of the rollup 1. A [Data Availability Layer](./rollup-interface/specs/interfaces/da.md) ("DA layer") which determines the set of transactions that are fed to the state transition function -1. A Zero Knowledge proving system (aka "Zero Knowledge Virtual Machine" or "ZKVM"), which takes the compiled rollup code and +1. A Zero Knowledge proving system (aka "Zero Knowledge Virtual Machine" or "zkVM"), which takes the compiled rollup code and produces succinct proofs that the logic has been executed correctly. One of the primary goals of the Sovereign SDK is to enable a clean separation of concerns between these three components. @@ -104,7 +104,7 @@ If you want to add support for a new data availability layer, the easiest way to Adapters contain the logic integrating 3rd party codebases into the Sovereign SDK. Over time, we expect Sovereign SDK to have adapters for almost all Data Availability Layers and LLVM-compatible proof systems. Currently, we -maintain adapters for [`Risc0`](https://www.risczero.com) (a ZKVM) and [`Celestia`](https://www.celestia.org) a (DA layer). +maintain adapters for [`Risc0`](https://www.risczero.com) (a zkVM) and [`Celestia`](https://www.celestia.org) a (DA layer). The Avail project also maintains an adapter for their DA layer, which can be found [here](https://github.com/availproject/avail-sovereign-da-adapter). [Chainway](https://chainway.xyz/) team also maintains an open-source Bitcoin DA adapter for their Sovereign Rollup on Bitcoin, which can be found [here](https://github.com/chainwayxyz/bitcoin-da). diff --git a/examples/const-rollup-config/README.md b/examples/const-rollup-config/README.md index bb59d00fc..5fe52531d 100644 --- a/examples/const-rollup-config/README.md +++ b/examples/const-rollup-config/README.md @@ -2,7 +2,7 @@ In Sovereign, many state transition functions require consensus critical configuration. For example, rollups on Celestia need to configure a namespace which they check for data. This consensus critical configuration needs to be available -to packages at compile time, so that it is baked into the binary which is fed to the ZKVM. Otherwise, a malicious +to packages at compile time, so that it is baked into the binary which is fed to the zkVM. Otherwise, a malicious prover might be able to overwrite this configuration at runtime and create valid-looking proofs that were run over the wrong namespace. diff --git a/examples/demo-rollup/README.md b/examples/demo-rollup/README.md index 957831490..776d604c3 100644 --- a/examples/demo-rollup/README.md +++ b/examples/demo-rollup/README.md @@ -47,7 +47,7 @@ This is a demo full node running a simple Sovereign SDK rollup on [Celestia](htt ## What is This? -This demo shows how to integrate a State Transition Function (STF) with a Data Availability (DA) layer and a ZKVM to create a full +This demo shows how to integrate a State Transition Function (STF) with a Data Availability (DA) layer and a zkVM to create a full zk-rollup. The code in this repository corresponds to running a full-node of the rollup, which executes every transaction. If you want to see the logic for _proof generation_, check out the [demo-prover](../demo-prover/) package instead. @@ -333,7 +333,7 @@ The above setup runs Celestia node locally to avoid any external network depende ## How to Customize This Example -Any time you change out the state transition function, ZKVM, or DA layer of your rollup, you'll +Any time you change out the state transition function, zkVM, or DA layer of your rollup, you'll need to tweak this full-node code. At the very least, you'll need to modify the dependencies. In most cases, your full node will also need to be aware of the STF's initialization logic, and how it exposes RPC. diff --git a/examples/demo-rollup/src/rollup.rs b/examples/demo-rollup/src/rollup.rs index cdaec16b6..bcad9f83b 100644 --- a/examples/demo-rollup/src/rollup.rs +++ b/examples/demo-rollup/src/rollup.rs @@ -79,7 +79,7 @@ pub fn configure_prover( pub enum DemoProverConfig { /// Run the rollup verification logic inside the current process Simulate, - /// Run the rollup verifier in a zkvm executor + /// Run the rollup verifier in a zkVM executor Execute, /// Run the rollup verifier and create a SNARK of execution Prove, diff --git a/examples/demo-simple-stf/README.md b/examples/demo-simple-stf/README.md index c576f9e4c..167cf5f27 100644 --- a/examples/demo-simple-stf/README.md +++ b/examples/demo-simple-stf/README.md @@ -8,7 +8,7 @@ In this tutorial, we’ll build an STF which checks if the input data (called a The [State Transition Function interface](../../rollup-interface/specs/interfaces/stf.md) serves as the core component of our rollup, where the business logic will reside. -Implementations of this trait can be integrated with any ZKVM and DA Layer resulting in a fully functional rollup. To begin, we will create a structure called `CheckHashPreimageStf`, and implement the `StateTransitionFunction` trait for it. You can find the complete code in the `lib.rs` file, but we will go over the most important parts here: +Implementations of this trait can be integrated with any zkVM and DA Layer resulting in a fully functional rollup. To begin, we will create a structure called `CheckHashPreimageStf`, and implement the `StateTransitionFunction` trait for it. You can find the complete code in the `lib.rs` file, but we will go over the most important parts here: ```rust, ignore pub struct CheckHashPreimageStf {} diff --git a/examples/demo-stf/README.md b/examples/demo-stf/README.md index c15003b22..c0bbc94b7 100644 --- a/examples/demo-stf/README.md +++ b/examples/demo-stf/README.md @@ -28,7 +28,7 @@ interface](../../rollup-interface/specs/interfaces/stf.md) ("STF") , which speci a related trait called `State Transition Runner` ("STR") which tells a full node how to instantiate your abstract STF on a concrete machine. Strictly speaking, it's sufficient for a rollup to only implement the first interface. If you've done that, it's possible to integrate -with ZKVMs and DA Layers - but you'll have to customize your full node implementation a bit to deal with your particular rollup's +with zkVMs and DA Layers - but you'll have to customize your full node implementation a bit to deal with your particular rollup's configuration. By implementing the STR trait, we make it much easier for the full-node implementation to understand how to interact with the rollup generically - so we can keep our modifications to the node as minimal as possible. In this demo, we'll implement both traits. @@ -168,7 +168,7 @@ to import your custom STF! But, when you're building an STF it's useful to stick That way, you can minimize the changeset for your custom node implementation, which reduces the risk of bugs. To help you integrate with full node implementations, we provide standard tools for initializing an app (`StateTransitionRunner`). In this section, we'll briefly show how to use them. Again it is not strictly -required - just by implementing STF, you get the capability to integrate with DA layers and ZKVMs. But, using these structures +required - just by implementing STF, you get the capability to integrate with DA layers and zkVMs. But, using these structures makes you more compatible with full node implementations out of the box. ### Using State Transition Runner @@ -184,4 +184,4 @@ The State Transition Runner struct contains logic related to initialization and Whew, that was a lot of information. To recap, implementing your own state transition function is as simple as plugging a Runtime, a Transaction Verifier, and some Transaction Hooks into the pre-built app template. Once you've done that, -you can integrate with any DA layer and ZKVM to create a Sovereign Rollup. +you can integrate with any DA layer and zkVM to create a Sovereign Rollup. diff --git a/examples/demo-stf/src/genesis_config.rs b/examples/demo-stf/src/genesis_config.rs index c28b2aa22..01551e9b6 100644 --- a/examples/demo-stf/src/genesis_config.rs +++ b/examples/demo-stf/src/genesis_config.rs @@ -2,15 +2,13 @@ use std::convert::AsRef; use std::path::Path; use anyhow::Context as AnyhowContext; -#[cfg(feature = "experimental")] -use reth_primitives::Bytes; use serde::de::DeserializeOwned; use sov_accounts::AccountConfig; use sov_bank::BankConfig; use sov_chain_state::ChainStateConfig; use sov_cli::wallet_state::PrivateKeyAndAddress; #[cfg(feature = "experimental")] -use sov_evm::{AccountData, EvmConfig, SpecId}; +use sov_evm::EvmConfig; pub use sov_modules_api::default_context::DefaultContext; use sov_modules_api::Context; use sov_nft_module::NonFungibleTokenConfig; @@ -36,7 +34,7 @@ pub const DEMO_TOKEN_NAME: &str = "sov-demo-token"; /// ``` pub fn get_genesis_config( sequencer_da_address: Da::Address, - #[cfg(feature = "experimental")] evm_genesis_addresses: Vec, + #[cfg(feature = "experimental")] eth_signers: Vec, ) -> GenesisConfig { let token_deployer: PrivateKeyAndAddress = read_private_key(); @@ -44,7 +42,7 @@ pub fn get_genesis_config( token_deployer.address.clone(), sequencer_da_address, #[cfg(feature = "experimental")] - evm_genesis_addresses, + eth_signers, ) .expect("Unable to read genesis configuration") } @@ -52,7 +50,7 @@ pub fn get_genesis_config( fn create_genesis_config( sequencer_address: C::Address, sequencer_da_address: Da::Address, - #[cfg(feature = "experimental")] evm_genesis_addresses: Vec, + #[cfg(feature = "experimental")] eth_signers: Vec, ) -> anyhow::Result> { // This path will be injected as a parameter: #872 let bank_genesis_path = "../test-data/genesis/bank.json"; @@ -86,6 +84,12 @@ fn create_genesis_config( let chain_state_path = "../test-data/genesis/chain_state.json"; let chain_state_config: ChainStateConfig = read_json_file(chain_state_path)?; + #[cfg(feature = "experimental")] + let evm_path = "../test-data/genesis/evm.json"; + + #[cfg(feature = "experimental")] + let evm_config = get_evm_config(evm_path, eth_signers)?; + Ok(GenesisConfig::new( bank_config, sequencer_registry_config, @@ -94,7 +98,7 @@ fn create_genesis_config( value_setter_config, accounts_config, #[cfg(feature = "experimental")] - get_evm_config(evm_genesis_addresses), + evm_config, nft_config, )) } @@ -110,28 +114,21 @@ fn read_json_file>(path: P) -> anyhow::Resul Ok(config) } -// TODO: #840 #[cfg(feature = "experimental")] -fn get_evm_config(genesis_addresses: Vec) -> EvmConfig { - let data = genesis_addresses - .into_iter() - .map(|address| AccountData { - address, - balance: AccountData::balance(u64::MAX), - code_hash: AccountData::empty_code(), - code: Bytes::default(), - nonce: 0, - }) - .collect(); - - EvmConfig { - data, - chain_id: 1, - limit_contract_code_size: None, - spec: vec![(0, SpecId::SHANGHAI)].into_iter().collect(), - block_timestamp_delta: 1u64, - ..Default::default() +fn get_evm_config>( + evm_path: P, + signers: Vec, +) -> anyhow::Result { + let config: EvmConfig = read_json_file(evm_path)?; + let addresses: std::collections::HashSet = + config.data.iter().map(|acc| acc.address).collect(); + + // check if all the eth signer are in genesis. + for signer in signers { + assert!(addresses.contains(&signer)); } + + Ok(config) } pub fn read_private_key() -> PrivateKeyAndAddress { diff --git a/examples/demo-stf/src/hooks_impl.rs b/examples/demo-stf/src/hooks_impl.rs index c3f55d754..38e786a69 100644 --- a/examples/demo-stf/src/hooks_impl.rs +++ b/examples/demo-stf/src/hooks_impl.rs @@ -90,6 +90,13 @@ impl SlotHooks for Runtime { #[cfg(feature = "experimental")] self.evm .begin_slot_hook(slot_header.hash().into(), working_set); + + self.chain_state.begin_slot_hook( + slot_header, + validity_condition, + pre_state_root, + working_set, + ); } fn end_slot_hook( @@ -98,6 +105,8 @@ impl SlotHooks for Runtime { ) { #[cfg(feature = "experimental")] self.evm.end_slot_hook(working_set); + + self.chain_state.end_slot_hook(working_set); } } @@ -111,5 +120,8 @@ impl FinalizeHook for Runtime>, } -/// Represents the possible modes of execution for a zkvm program +/// Represents the possible modes of execution for a zkVM program pub enum ProofGenConfig where ST: StateTransitionFunction, { - /// The simulator runs the rollup verifier logic without even emulating the zkvm + /// The simulator runs the rollup verifier logic without even emulating the zkVM Simulate(StateTransitionVerifier), - /// The executor runs the rollup verification logic in the zkvm, but does not actually + /// The executor runs the rollup verification logic in the zkVM, but does not actually /// produce a zk proof Execute, - /// The prover runs the rollup verification logic in the zkvm and produces a zk proof + /// The prover runs the rollup verification logic in the zkVM and produces a zk proof Prover, } @@ -196,16 +196,16 @@ where state_transition_witness: slot_result.witness, }; vm.add_hint(transition_data); - - match config { - ProofGenConfig::Simulate(verifier) => { - verifier.run_block(vm.simulate_with_hints()).map_err(|e| { + tracing::info_span!("guest_execution").in_scope(|| match config { + ProofGenConfig::Simulate(verifier) => verifier + .run_block(vm.simulate_with_hints()) + .map_err(|e| { anyhow::anyhow!("Guest execution must succeed but failed with {:?}", e) - })?; - } - ProofGenConfig::Execute => vm.run(false)?, - ProofGenConfig::Prover => vm.run(true)?, - } + }) + .map(|_| ()), + ProofGenConfig::Execute => vm.run(false), + ProofGenConfig::Prover => vm.run(true), + })?; } let next_state_root = slot_result.state_root; diff --git a/module-system/README.md b/module-system/README.md index 9812c4b1c..61afa1a2a 100644 --- a/module-system/README.md +++ b/module-system/README.md @@ -241,25 +241,28 @@ which re-executes the transactions in a (more expensive) zk environment to creat workflow looks roughly like this: ```rust -// First, execute transactions natively to generate a witness for the zkvm -let native_rollup_instance = my_state_transition::::new(config); -let witness = Default::default() -native_rollup_instance.begin_slot(witness); -for batch in batches.cloned() { - native_rollup_instance.apply_batch(batch); +use sov_modules_api::DefaultContext; +fn main() { + // First, execute transactions natively to generate a witness for the zkVM + let native_rollup_instance = my_state_transition::::new(config); + let witness = Default::default(); + native_rollup_instance.begin_slot(witness); + for batch in batches.cloned() { + native_rollup_instance.apply_batch(batch); + } + let (_new_state_root, populated_witness) = native_rollup_instance.end_batch(); + + // Then, re-execute the state transitions in the zkVM using the witness + let proof = MyZkvm::prove(|| { + let zk_rollup_instance = my_state_transition::::new(config); + zk_rollup_instance.begin_slot(populated_witness); + for batch in batches { + zk_rollup_instance.apply(batch); + } + let (new_state_root, _) = zk_rollup_instance.end_batch(); + MyZkvm::commit(new_state_root) + }); } -let (_new_state_root, populated_witness) = native_rollup_instance.end_batch(); - -// Then, re-execute the state transitions in the zkvm using the witness -let proof = MyZkvm::prove(|| { - let zk_rollup_instance = my_state_transition::::new(config); - zk_rollup_instance.begin_slot(populated_witness); - for batch in batches { - zk_rollup_instance.apply(batch); - } - let (new_state_root, _) = zk_rollup_instance.end_batch(); - MyZkvm::commit(new_state_root) -}) ``` This distinction between native _execution_ and zero-knowledge _re-execution_ is deeply baked into the Module System. We take the @@ -272,16 +275,19 @@ The most important trait we use to enable this abstraction is the `Spec` trait. ```rust pub trait Spec { + type Address; type Storage; + type PrivateKey; type PublicKey; type Hasher; type Signature; + type Witness; } ``` As you can see, a `Spec` for a rollup specifies the concrete types that will be used for many kinds of cryptographic operations. -That way, you can define your business logic in terms of _abstract_ cryptography, and then instantiate it with cryptography which -is efficient in your particular choice of ZKVM. +That way, you can define your business logic in terms of _abstract_ cryptography, and then instantiate it with cryptography, which +is efficient in your particular choice of zkVM. In addition to the `Spec` trait, the Module System provides a simple `Context` trait which is defined like this: @@ -318,7 +324,7 @@ Similarly, since each of the banks helper functions is automatically generic ove can abstract away the distinctions between `zk` and `native` execution. For example, when a rollup is running in native mode its `Storage` type will almost certainly be [`ProverStorage`](./sov-state/src/prover_storage.rs), which holds its data in a Merkle tree backed by RocksDB. But if you're running in zk mode the `Storage` type will instead be [`ZkStorage`](./sov-state/src/zk_storage.rs), which reads -its data from a set of "hints" provided by the prover. Because all of the rollups modules are generic, none of them need to worry +its data from a set of "hints" provided by the prover. Because all the rollups modules are generic, none of them need to worry about this distinction. For more information on `Context` and `Spec`, and to see some example implementations, check out the [`sov_modules_api`](./sov-modules-api/) docs. diff --git a/module-system/module-implementations/sov-evm/src/lib.rs b/module-system/module-implementations/sov-evm/src/lib.rs index a53d197aa..a503d7fed 100644 --- a/module-system/module-implementations/sov-evm/src/lib.rs +++ b/module-system/module-implementations/sov-evm/src/lib.rs @@ -52,7 +52,7 @@ mod experimental { pub(crate) const MIN_CREATE_GAS: u64 = 53_000u64; /// Evm account. - #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] pub struct AccountData { /// Account address. pub address: Address, @@ -79,7 +79,7 @@ mod experimental { } /// Genesis configuration. - #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] pub struct EvmConfig { /// Genesis accounts. pub data: Vec, diff --git a/module-system/module-implementations/sov-evm/src/tests/config_tests.rs b/module-system/module-implementations/sov-evm/src/tests/config_tests.rs new file mode 100644 index 000000000..1d6fd9629 --- /dev/null +++ b/module-system/module-implementations/sov-evm/src/tests/config_tests.rs @@ -0,0 +1,54 @@ +use std::str::FromStr; + +use reth_primitives::{Address, Bytes}; +use revm::primitives::SpecId; + +use crate::{AccountData, EvmConfig}; + +#[test] +fn test_config_serialization() { + let address = Address::from_str("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266").unwrap(); + let config = EvmConfig { + data: vec![AccountData { + address, + balance: AccountData::balance(u64::MAX), + code_hash: AccountData::empty_code(), + code: Bytes::default(), + nonce: 0, + }], + chain_id: 1, + limit_contract_code_size: None, + spec: vec![(0, SpecId::SHANGHAI)].into_iter().collect(), + block_timestamp_delta: 1u64, + ..Default::default() + }; + + let data = r#" + { + "data":[ + { + "address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "balance":"0xffffffffffffffff", + "code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "code":"0x", + "nonce":0 + }], + "chain_id":1, + "limit_contract_code_size":null, + "spec":{ + "0":"SHANGHAI" + }, + "coinbase":"0x0000000000000000000000000000000000000000", + "starting_base_fee":7, + "block_gas_limit":30000000, + "genesis_timestamp":0, + "block_timestamp_delta":1, + "base_fee_params":{ + "max_change_denominator":8, + "elasticity_multiplier":2 + } + }"#; + + let parsed_config: EvmConfig = serde_json::from_str(data).unwrap(); + assert_eq!(config, parsed_config) +} diff --git a/module-system/module-implementations/sov-evm/src/tests/mod.rs b/module-system/module-implementations/sov-evm/src/tests/mod.rs index 778478f9f..da5a19d50 100644 --- a/module-system/module-implementations/sov-evm/src/tests/mod.rs +++ b/module-system/module-implementations/sov-evm/src/tests/mod.rs @@ -1,5 +1,6 @@ mod call_tests; mod cfg_tests; +mod config_tests; pub(crate) mod dev_signer; mod genesis_tests; mod hooks_tests; diff --git a/module-system/sov-modules-api/src/default_context.rs b/module-system/sov-modules-api/src/default_context.rs index 8fccbcc41..6607cdae0 100644 --- a/module-system/sov-modules-api/src/default_context.rs +++ b/module-system/sov-modules-api/src/default_context.rs @@ -51,9 +51,9 @@ pub struct ZkDefaultContext { impl Spec for ZkDefaultContext { type Address = Address; type Storage = ZkStorage; - type PublicKey = DefaultPublicKey; #[cfg(feature = "native")] type PrivateKey = DefaultPrivateKey; + type PublicKey = DefaultPublicKey; type Hasher = sha2::Sha256; type Signature = DefaultSignature; type Witness = ArrayWitness; diff --git a/module-system/sov-modules-api/src/lib.rs b/module-system/sov-modules-api/src/lib.rs index 1230a089a..085b3e7b1 100644 --- a/module-system/sov-modules-api/src/lib.rs +++ b/module-system/sov-modules-api/src/lib.rs @@ -196,7 +196,7 @@ pub trait PrivateKey: /// over a Context, rollup developers can easily optimize their code for different environments /// by simply swapping out the Context (and by extension, the Spec). /// -/// For example, a rollup running in a STARK-based zkvm like Risc0 might pick Sha256 or Poseidon as its preferred hasher, +/// For example, a rollup running in a STARK-based zkVM like Risc0 might pick Sha256 or Poseidon as its preferred hasher, /// while a rollup running in an elliptic-curve based SNARK such as `Placeholder` from the =nil; foundation might /// prefer a Pedersen hash. By using a generic Context and Spec, a rollup developer can trivially customize their /// code for either (or both!) of these environments without touching their module implementations. @@ -221,6 +221,10 @@ pub trait Spec { /// Authenticated state storage used by the rollup. Typically some variant of a merkle-patricia trie. type Storage: Storage + Send + Sync; + /// The public key used for digital signatures + #[cfg(feature = "native")] + type PrivateKey: PrivateKey; + /// The public key used for digital signatures #[cfg(feature = "native")] type PublicKey: PublicKey + ::schemars::JsonSchema + FromStr; @@ -228,10 +232,6 @@ pub trait Spec { #[cfg(not(feature = "native"))] type PublicKey: PublicKey; - /// The public key used for digital signatures - #[cfg(feature = "native")] - type PrivateKey: PrivateKey; - /// The hasher preferred by the rollup, such as Sha256 or Poseidon. type Hasher: Digest; diff --git a/module-system/sov-state/README.md b/module-system/sov-state/README.md index ebcd9c4a3..0d7af99b0 100644 --- a/module-system/sov-state/README.md +++ b/module-system/sov-state/README.md @@ -2,7 +2,7 @@ This crate provides abstractions specifically designed for storing and retrieving data from a permanent storage, tailored to be used within the Module System. -## High level explanation +## High-level explanation At a high level, the crate offers two main abstractions that module developers can utilize to access data: @@ -37,13 +37,13 @@ Module developers can interact with the `WorkingSet`, `StateValue`, and `StateMa The above API is used in the following way: -```Rust +```rust state.value.set(&some_value, working_set); let maybe_value = state.value.get(working_set); ``` -## Low level explanation +## Low-level explanation It's important to note that an understanding of this section is not necessarily required for efficient usage of the `sov-state`. diff --git a/rollup-interface/specs/interfaces/stf.md b/rollup-interface/specs/interfaces/stf.md index aaf214e43..d7d4d0018 100644 --- a/rollup-interface/specs/interfaces/stf.md +++ b/rollup-interface/specs/interfaces/stf.md @@ -39,8 +39,8 @@ adaptive gas pricing depending on prover throughput. - **Arguments** | Name | Type | Description | -| ------------------ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| witness | WITNESS | The witness to be used to process this slot. In prover mode, the witness argument is an empty struct which is populated with "hints" for the ZKVM during execution. In ZK mode, the argument is the pre-populated set of hints. | +| ------------------ | ------------------ |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| witness | WITNESS | The witness to be used to process this slot. In prover mode, the witness argument is an empty struct which is populated with "hints" for the zkVM during execution. In ZK mode, the argument is the pre-populated set of hints. | | slot_header | BLOCK_HEADER | The header of the block on the DA layer | | validity_condition | VALIDITY_CONDITION | Data for any extra checks which must be made by light clients before accepting the state transition from this slot. For example, if the DA layer uses a verkle tree which is too expensive to open in-circuit, this might contain a merkle root of the observed slot data - which light clients would need to check "out-of-band". | | blobs | BLOB_TRANSACTIONS | An iterator over the blobs included in this slot | @@ -115,7 +115,7 @@ state of a particular instance of the state transition function. ### Witness -A custom type for each state transition function containing the hints that are passed to the ZKVM. +A custom type for each state transition function containing the hints that are passed to the zkVM. ## Optional Methods diff --git a/rollup-interface/specs/interfaces/zkvm.md b/rollup-interface/specs/interfaces/zkvm.md index 6052e8911..d6b01d07a 100644 --- a/rollup-interface/specs/interfaces/zkvm.md +++ b/rollup-interface/specs/interfaces/zkvm.md @@ -1,4 +1,4 @@ -# ZKVM +# zkVM The sovereign SDK is designed to support any zkVM capable of running Rust code. However, VMs must be capable of supporting a standard set of APIs. @@ -7,7 +7,7 @@ However, VMs must be capable of supporting a standard set of APIs. This specification does *not* define any standards relating to performance: proof size, prover work, verification time, latency. This omission should not be understood to imply -that the SDK will work equally well for all choice of proof system. However, since, the SDK will *function* correctly when +that the SDK will work equally well for all choice of proof system. However, the SDK will *function* correctly when defined in any sound proof system, we don't define any specific requirements. We strongly suggest that users consider a performant VM such as Risc0. @@ -59,17 +59,16 @@ when proof verification fails. Expressed in Rust, zkVM would be a `trait` that looked something like the following: - ```rust -pub trait ZkVM { - type CodeCommitment: PartialEq + Clone; - type Proof: Encode + Decode; - type Error; - - fn log(item: T); - fn verify( - proof: Self::Proof, - code_commitment: &Self::CodeCommitment, - ) -> Result; +pub trait Zkvm { + type CodeCommitment: PartialEq + Clone; + type Proof: Encode + Decode; + type Error; + + fn log(item: T); + fn verify( + proof: Self::Proof, + code_commitment: &Self::CodeCommitment, + ) -> Result; } ``` diff --git a/rollup-interface/specs/overview.md b/rollup-interface/specs/overview.md index 2fa5b8f5e..163ce2fee 100644 --- a/rollup-interface/specs/overview.md +++ b/rollup-interface/specs/overview.md @@ -17,7 +17,7 @@ Conceptually, adding a block to a Sovereign SDK chain happens in three steps. Fi the L1 chain. As soon as the blob is finalized on L1, it is logically final. Immediately after the L1 block is finalized, full nodes of the rollup scan through it and process all relevant data blobs in the order that they appear, generating a new rollup state root. At this point, the block is subjectively finalized from the perspective of all full nodes. -Last but not least, prover nodes (full nodes running inside a ZKVM) perform roughly the same process as full nodes - +Last but not least, prover nodes (full nodes running inside a zkVM) perform roughly the same process as full nodes - scanning through the DA block and processing all of the batches in order - producing proofs and posting them on chain. (Proofs need to be posted on chain if the rollup wants to incentivize provers - otherwise, it's impossible to tell which prover was first to process a given batch). diff --git a/rollup-interface/src/state_machine/README.md b/rollup-interface/src/state_machine/README.md index 2eb8e0102..b467999d2 100644 --- a/rollup-interface/src/state_machine/README.md +++ b/rollup-interface/src/state_machine/README.md @@ -1,7 +1,7 @@ # State Machine This folder contains items that *may* interact with the state machine of the application. -This includes the SDK's core traits (DaApp, StateTransitionFunction, ZkVM), serialization, +This includes the SDK's core traits (DaApp, StateTransitionFunction, zkVM), serialization, cryptographic data structures, etc. Items in this folder are consensus critical, and should be modified only with appropriate care. diff --git a/rollup-interface/src/state_machine/mocks/da.rs b/rollup-interface/src/state_machine/mocks/da.rs index 20c38d2eb..d4229fb3c 100644 --- a/rollup-interface/src/state_machine/mocks/da.rs +++ b/rollup-interface/src/state_machine/mocks/da.rs @@ -373,7 +373,7 @@ impl DaVerifier for MockDaVerifier { _inclusion_proof: ::InclusionMultiProof, _completeness_proof: ::CompletenessProof, ) -> Result<::ValidityCondition, Self::Error> { - Ok(MockValidityCond { is_valid: true }) + Ok(Default::default()) } } diff --git a/rollup-interface/src/state_machine/mocks/validity_condition.rs b/rollup-interface/src/state_machine/mocks/validity_condition.rs index e96b72df4..f7ad4ceb2 100644 --- a/rollup-interface/src/state_machine/mocks/validity_condition.rs +++ b/rollup-interface/src/state_machine/mocks/validity_condition.rs @@ -9,22 +9,20 @@ use crate::zk::{ValidityCondition, ValidityConditionChecker}; /// A trivial test validity condition structure that only contains a boolean #[derive( - Debug, - BorshDeserialize, - BorshSerialize, - Serialize, - Deserialize, - PartialEq, - Clone, - Copy, - Default, - Eq, + Debug, BorshDeserialize, BorshSerialize, Serialize, Deserialize, PartialEq, Clone, Copy, Eq, )] pub struct MockValidityCond { /// The associated validity condition field. If it is true, the validity condition is verified pub is_valid: bool, } +// Validity conditions should usually be valid +impl Default for MockValidityCond { + fn default() -> Self { + Self { is_valid: true } + } +} + impl ValidityCondition for MockValidityCond { type Error = Error; fn combine(&self, rhs: Self) -> Result { diff --git a/rollup-interface/src/state_machine/stf.rs b/rollup-interface/src/state_machine/stf.rs index 3ab0b5508..a6475d5e8 100644 --- a/rollup-interface/src/state_machine/stf.rs +++ b/rollup-interface/src/state_machine/stf.rs @@ -16,7 +16,7 @@ pub mod fuzzing; /// The configuration of a full node of the rollup which creates zk proofs. pub struct ProverConfig; /// The configuration used to initialize the "Verifier" of the state transition function -/// which runs inside of the zkvm. +/// which runs inside of the zkVM. pub struct ZkConfig; /// The configuration of a standard full node of the rollup which does not create zk proofs pub struct StandardConfig; diff --git a/rollup-interface/src/state_machine/zk/mod.rs b/rollup-interface/src/state_machine/zk/mod.rs index d203a8656..0414f6dce 100644 --- a/rollup-interface/src/state_machine/zk/mod.rs +++ b/rollup-interface/src/state_machine/zk/mod.rs @@ -1,4 +1,4 @@ -//! Defines the traits that must be implemented by zkVMs. A ZKVM like Risc0 consists of two components, +//! Defines the traits that must be implemented by zkVMs. A zkVM like Risc0 consists of two components, //! a "guest" and a "host". The guest is the zkVM program itself, and the host is the physical machine on //! which the zkVM is running. Both the guest and the host are required to implement the [`Zkvm`] trait, in //! addition to the specialized [`ZkvmGuest`] and [`ZkvmHost`] trait which is appropriate to that environment. @@ -31,7 +31,7 @@ pub trait ZkvmHost: Zkvm { /// Run the guest in the true zk environment using the provided hints. /// - /// This runs the guest binary compiled for the ZKVM target, optionally + /// This runs the guest binary compiled for the zkVM target, optionally /// creating a SNARK of correct execution. Running the true guest binary comes /// with some mild performance overhead and is not as easy to debug as [`simulate_with_hints`](ZkvmHost::simulate_with_hints). fn run(&mut self, with_proof: bool) -> Result<(), anyhow::Error>;