From bddb6d4da941e1d7423a5872edf5dcf149c68452 Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Oct 2026 19:23:31 +0100 Subject: [PATCH 1/4] feat(proof_engine): accept zkboost mock proofs with --proof-engine-mock-proofs zkboost v0.12.0 mock zkVMs emit an SSZ `MockProof { public_values, proof }` with random proof bytes, which the ERE verifiers reject. Extend `MockProofEngine` to accept these proofs for the configured proof types when their public values match the proof's public input, and select it at runtime with the devnet-only `--proof-engine-mock-proofs` flag. This lets an EIP-8025 devnet run without a GPU prover. --- beacon_node/proof_engine/src/config.rs | 30 ++++- beacon_node/proof_engine/src/test_utils.rs | 133 ++++++++++++++++++++- beacon_node/src/cli.rs | 12 ++ beacon_node/src/config.rs | 7 ++ lighthouse/tests/beacon_node.rs | 29 +++++ 5 files changed, 205 insertions(+), 6 deletions(-) diff --git a/beacon_node/proof_engine/src/config.rs b/beacon_node/proof_engine/src/config.rs index e2f23d8e702..77267e7f267 100644 --- a/beacon_node/proof_engine/src/config.rs +++ b/beacon_node/proof_engine/src/config.rs @@ -1,6 +1,6 @@ //! Configuration for mapping EIP-8025 proof types to ERE zkVM verifiers. -use crate::{ProofEngine, ProofEngineError}; +use crate::{ProofEngine, ProofEngineError, test_utils::MockProofEngine}; use serde::{Deserialize, Deserializer, Serialize, de::Error as _}; use std::{collections::HashSet, str::FromStr}; use types::execution::ProofType; @@ -48,6 +48,10 @@ const DEFAULT_ZESU_ZISK_PROGRAM_VK: &str = #[derive(Debug, Clone, Serialize)] pub struct ProofEngineConfig { execution_proofs: Vec, + /// Accept zkboost mock proofs of the configured proof types in place of the ERE verifiers. + /// Devnets only. + #[serde(skip)] + mock_proofs: bool, /// Ready-built engine returned by [`Self::build_engine`] instead of the configured /// verifiers. Never serialized and only present with the `test-utils` feature, so tests can /// inject a mock without an `ere-verifier` build. @@ -59,7 +63,7 @@ pub struct ProofEngineConfig { /// Equality covers the configured verifiers only; an injected test engine is not compared. impl PartialEq for ProofEngineConfig { fn eq(&self, other: &Self) -> bool { - self.execution_proofs == other.execution_proofs + self.execution_proofs == other.execution_proofs && self.mock_proofs == other.mock_proofs } } @@ -91,6 +95,7 @@ impl ProofEngineConfig { Ok(Self { execution_proofs, + mock_proofs: false, #[cfg(feature = "test-utils")] engine: None, }) @@ -101,6 +106,18 @@ impl ProofEngineConfig { &self.execution_proofs } + /// Accept the mock proofs of zkboost's mock zkVMs, for devnets without a prover. See + /// [`MockProofEngine::zkboost`]. + pub fn with_mock_proofs(mut self) -> Self { + self.mock_proofs = true; + self + } + + /// Whether zkboost mock proofs are accepted in place of the ERE verifiers. + pub fn mock_proofs(&self) -> bool { + self.mock_proofs + } + /// Yield `engine` from [`Self::build_engine`] instead of building the configured verifiers. #[cfg(feature = "test-utils")] pub fn with_engine(mut self, engine: ProofEngine) -> Self { @@ -109,13 +126,20 @@ impl ProofEngineConfig { } /// Build the proof engine this configuration describes: an injected test engine if one was - /// supplied, otherwise the ERE verifiers, which need the `ere-verifier` feature. + /// supplied, the zkboost mock-proof engine in mock mode, otherwise the ERE verifiers, which + /// need the `ere-verifier` feature. pub fn build_engine(&self) -> Result { #[cfg(feature = "test-utils")] if let Some(engine) = &self.engine { return Ok(engine.clone()); } + if self.mock_proofs { + return Ok(ProofEngine::new(MockProofEngine::zkboost( + self.execution_proofs.iter().map(|config| config.proof_type), + ))); + } + #[cfg(feature = "ere-verifier")] { crate::ere::EreProofEngine::new(self.clone()).map(ProofEngine::new) diff --git a/beacon_node/proof_engine/src/test_utils.rs b/beacon_node/proof_engine/src/test_utils.rs index 485d32b2076..36dcda22f91 100644 --- a/beacon_node/proof_engine/src/test_utils.rs +++ b/beacon_node/proof_engine/src/test_utils.rs @@ -1,13 +1,17 @@ -//! Test utilities for proof-engine consumers. +//! Mock proof engines, for tests and for devnets without a prover. use crate::{ProofEngineError, ProofEngineT, ProofVerificationOutcome}; +use ssz::{Encode, SszDecoderBuilder}; use std::collections::HashSet; -use types::execution::ExecutionProof; +use types::execution::{ExecutionProof, ProofType}; -/// Deterministic proof engine used by beacon-chain tests. +/// Deterministic proof engine used by beacon-chain tests, and by devnets through +/// `--proof-engine-mock-proofs`. #[derive(Debug, Default)] pub struct MockProofEngine { valid_proof_data: HashSet>, + /// Proof types whose zkboost mock proofs are accepted. `None` accepts no mock proof. + mock_proof_types: Option>, } impl MockProofEngine { @@ -15,8 +19,33 @@ impl MockProofEngine { pub fn new(valid_proof_data: impl IntoIterator>) -> Self { Self { valid_proof_data: valid_proof_data.into_iter().collect(), + mock_proof_types: None, } } + + /// Construct a mock that accepts the mock proofs of zkboost's mock zkVMs for `proof_types`. + /// + /// A zkboost mock proof is the SSZ container + /// `MockProof { public_values: List[byte], proof: List[byte] }` whose `proof` is random bytes. + /// It is valid when its public values are the serialized public input of the proof, the bytes + /// a real guest commits; `proof` is not checked. + pub fn zkboost(proof_types: impl IntoIterator) -> Self { + Self { + valid_proof_data: HashSet::new(), + mock_proof_types: Some(proof_types.into_iter().collect()), + } + } +} + +/// Return the public values of `proof_data` if it decodes as a zkboost `MockProof`. +fn mock_proof_public_values(proof_data: &[u8]) -> Option> { + let mut builder = SszDecoderBuilder::new(proof_data); + builder.register_type::>().ok()?; + builder.register_type::>().ok()?; + let mut decoder = builder.build().ok()?; + let public_values = decoder.decode_next::>().ok()?; + decoder.decode_next::>().ok()?; + Some(public_values) } impl ProofEngineT for MockProofEngine { @@ -24,6 +53,17 @@ impl ProofEngineT for MockProofEngine { &self, proof: &ExecutionProof, ) -> Result { + if let Some(mock_proof_types) = &self.mock_proof_types { + if !mock_proof_types.contains(&proof.proof_type) { + return Err(ProofEngineError::UnconfiguredProofType(proof.proof_type)); + } + if mock_proof_public_values(proof.proof_data.as_ref()) + .is_some_and(|public_values| public_values == proof.public_input.as_ssz_bytes()) + { + return Ok(ProofVerificationOutcome::Valid); + } + } + Ok( if self.valid_proof_data.contains(proof.proof_data.as_ref()) { ProofVerificationOutcome::Valid @@ -69,4 +109,91 @@ mod tests { ProofVerificationOutcome::Invalid ); } + + /// The SSZ `MockProof` of zkboost: the offsets of both lists, then the public values and the + /// proof. + fn zkboost_mock_proof(public_values: &[u8], proof: &[u8]) -> Vec { + let mut encoded = Vec::new(); + encoded.extend_from_slice(&8u32.to_le_bytes()); + encoded.extend_from_slice(&(8 + public_values.len() as u32).to_le_bytes()); + encoded.extend_from_slice(public_values); + encoded.extend_from_slice(proof); + encoded + } + + #[test] + fn zkboost_mock_proofs_verify_their_public_values() { + let proof_engine = ProofEngine::new(MockProofEngine::zkboost([ProofType::EthrexSP1])); + let proof = |proof_type, proof_data| { + ExecutionProof::new( + ProofData::new(proof_data).expect("proof data within bound"), + proof_type, + Hash256::repeat_byte(0x33), + 1, + ) + }; + let public_values = proof(ProofType::EthrexSP1, vec![1]) + .public_input + .as_ssz_bytes(); + let verify = |proof_type, proof_data| { + proof_engine.verify_execution_proof(&proof(proof_type, proof_data)) + }; + + assert_eq!( + verify( + ProofType::EthrexSP1, + zkboost_mock_proof(&public_values, &[0xbb; 64]) + ) + .expect("mock verification succeeds"), + ProofVerificationOutcome::Valid + ); + + let mut other_public_values = public_values.clone(); + other_public_values[0] ^= 1; + for proof_data in [ + zkboost_mock_proof(&other_public_values, &[0xbb; 64]), + zkboost_mock_proof(&public_values[..public_values.len() - 1], &[0xbb; 64]), + vec![0xff; 64], + ] { + assert_eq!( + verify(ProofType::EthrexSP1, proof_data).expect("mock verification succeeds"), + ProofVerificationOutcome::Invalid + ); + } + + assert!(matches!( + verify( + ProofType::RethSP1, + zkboost_mock_proof(&public_values, &[0xbb; 64]) + ), + Err(ProofEngineError::UnconfiguredProofType(ProofType::RethSP1)) + )); + } + + #[test] + fn exact_proof_data_mock_rejects_zkboost_mock_proofs() { + let proof = ExecutionProof::new( + ProofData::new(vec![1]).expect("proof data within bound"), + ProofType::RethSP1, + Hash256::default(), + 1, + ); + let mock_proof = ExecutionProof::new( + ProofData::new(zkboost_mock_proof( + &proof.public_input.as_ssz_bytes(), + &[0xbb; 64], + )) + .expect("proof data within bound"), + ProofType::RethSP1, + Hash256::default(), + 1, + ); + + assert_eq!( + ProofEngine::new(MockProofEngine::default()) + .verify_execution_proof(&mock_proof) + .expect("mock verification succeeds"), + ProofVerificationOutcome::Invalid + ); + } } diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index 419ad5315a7..247b012b9e5 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -860,6 +860,18 @@ pub fn cli_app() -> Command { .action(ArgAction::Set) .display_order(0) ) + .arg( + Arg::new("proof-engine-mock-proofs") + .long("proof-engine-mock-proofs") + .action(ArgAction::SetTrue) + .help_heading(FLAG_HEADER) + .help("DEVNETS ONLY. Accept the mock proofs of zkboost's mock zkVMs (an SSZ \ + MockProof) for the proof types of --proof-engine, in place of verifying \ + them with ERE. A mock proof is valid when its public values match the \ + proof's public input; its proof bytes are not checked.") + .requires("proof-engine") + .display_order(0) + ) .arg( Arg::new("execution-jwt") .long("execution-jwt") diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 1bfd38c77ad..23087846b73 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -308,6 +308,13 @@ pub fn get_config( json.parse::() .map_err(|e| format!("Invalid --proof-engine `{path}`: {e}"))? }); + if cli_args.get_flag("proof-engine-mock-proofs") { + warn!("Accepting zkboost mock execution proofs without verifying them. Devnets only"); + client_config.proof_engine = client_config + .proof_engine + .take() + .map(ProofEngineConfig::with_mock_proofs); + } client_config.network.enable_execution_proof = true; } diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index c44480ed907..e8931b414e0 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -510,6 +510,35 @@ fn run_execution_jwt_secret_key_is_persisted() { assert_eq!(file_jwt_secret_key, jwt_secret_key); }); } +#[test] +fn proof_engine_mock_proofs_flag() { + CommandLineTest::new() + .flag("proof-engine", None) + .flag("proof-engine-mock-proofs", None) + .run_with_zero_port() + .with_config(|config| { + assert!( + config + .proof_engine + .as_ref() + .is_some_and(|proof_engine| proof_engine.mock_proofs()) + ); + }); +} +#[test] +fn proof_engine_without_mock_proofs_flag() { + CommandLineTest::new() + .flag("proof-engine", None) + .run_with_zero_port() + .with_config(|config| { + assert!( + config + .proof_engine + .as_ref() + .is_some_and(|proof_engine| !proof_engine.mock_proofs()) + ); + }); +} /// A proof-only node verifies execution in-process instead of driving an engine. #[test] fn proof_only_node_builds_without_execution_layer() { From 256e123811926d380a1b2df27a84227b6a68a6f2 Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Oct 2026 19:51:32 +0100 Subject: [PATCH 2/4] Decode zkboost mock proofs with a MockProof SSZ container Mirror zkboost's `MockProof { public_values, proof }` and derive its SSZ codec instead of decoding the two lists by hand. A layout test pins the encoding to the one zkboost tests. --- beacon_node/proof_engine/Cargo.toml | 2 +- beacon_node/proof_engine/src/test_utils.rs | 58 ++++++++++++++-------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/beacon_node/proof_engine/Cargo.toml b/beacon_node/proof_engine/Cargo.toml index 909dfb5b7d6..4313e514827 100644 --- a/beacon_node/proof_engine/Cargo.toml +++ b/beacon_node/proof_engine/Cargo.toml @@ -12,6 +12,7 @@ test-utils = [] [dependencies] ethereum_serde_utils = { workspace = true } ethereum_ssz = { workspace = true } +ethereum_ssz_derive = { workspace = true } hex = { workspace = true } metrics = { workspace = true } serde = { workspace = true } @@ -25,7 +26,6 @@ sha2 = { workspace = true, optional = true } tar = { version = "0.4", default-features = false, optional = true } [dev-dependencies] -ethereum_ssz_derive = { workspace = true } execution_layer = { workspace = true } ssz_types = { workspace = true } tree_hash = { workspace = true } diff --git a/beacon_node/proof_engine/src/test_utils.rs b/beacon_node/proof_engine/src/test_utils.rs index 36dcda22f91..6c80b831d9e 100644 --- a/beacon_node/proof_engine/src/test_utils.rs +++ b/beacon_node/proof_engine/src/test_utils.rs @@ -1,7 +1,8 @@ //! Mock proof engines, for tests and for devnets without a prover. use crate::{ProofEngineError, ProofEngineT, ProofVerificationOutcome}; -use ssz::{Encode, SszDecoderBuilder}; +use ssz::{Decode, Encode}; +use ssz_derive::{Decode, Encode}; use std::collections::HashSet; use types::execution::{ExecutionProof, ProofType}; @@ -37,15 +38,14 @@ impl MockProofEngine { } } -/// Return the public values of `proof_data` if it decodes as a zkboost `MockProof`. -fn mock_proof_public_values(proof_data: &[u8]) -> Option> { - let mut builder = SszDecoderBuilder::new(proof_data); - builder.register_type::>().ok()?; - builder.register_type::>().ok()?; - let mut decoder = builder.build().ok()?; - let public_values = decoder.decode_next::>().ok()?; - decoder.decode_next::>().ok()?; - Some(public_values) +/// The `proof_data` of a zkboost mock proof, as `MockProof` in eth-act/zkboost +/// (`crates/types/src/execution_proof.rs`). The `proof_data` list bounds both lists. +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +pub struct MockProof { + /// The public values that the guest commits, as a verifier of a real proof returns them. + pub public_values: Vec, + /// Random bytes in place of a real proof. + pub proof: Vec, } impl ProofEngineT for MockProofEngine { @@ -57,9 +57,9 @@ impl ProofEngineT for MockProofEngine { if !mock_proof_types.contains(&proof.proof_type) { return Err(ProofEngineError::UnconfiguredProofType(proof.proof_type)); } - if mock_proof_public_values(proof.proof_data.as_ref()) - .is_some_and(|public_values| public_values == proof.public_input.as_ssz_bytes()) - { + if MockProof::from_ssz_bytes(proof.proof_data.as_ref()).is_ok_and(|mock_proof| { + mock_proof.public_values == proof.public_input.as_ssz_bytes() + }) { return Ok(ProofVerificationOutcome::Valid); } } @@ -110,15 +110,31 @@ mod tests { ); } - /// The SSZ `MockProof` of zkboost: the offsets of both lists, then the public values and the - /// proof. fn zkboost_mock_proof(public_values: &[u8], proof: &[u8]) -> Vec { - let mut encoded = Vec::new(); - encoded.extend_from_slice(&8u32.to_le_bytes()); - encoded.extend_from_slice(&(8 + public_values.len() as u32).to_le_bytes()); - encoded.extend_from_slice(public_values); - encoded.extend_from_slice(proof); - encoded + MockProof { + public_values: public_values.to_vec(), + proof: proof.to_vec(), + } + .as_ssz_bytes() + } + + /// The encoding matches the layout zkboost tests: the offsets of both lists, then the public + /// values and the proof. + #[test] + fn mock_proof_layout_matches_zkboost() { + let mock_proof = MockProof { + public_values: vec![0xaa; 3], + proof: vec![0xbb; 2], + }; + let encoded = mock_proof.as_ssz_bytes(); + assert_eq!( + encoded, + [8, 0, 0, 0, 11, 0, 0, 0, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb] + ); + assert_eq!( + MockProof::from_ssz_bytes(&encoded).expect("valid mock proof"), + mock_proof + ); } #[test] From ede59d2d5e6725706b0a249ff70b12519bce49ca Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Oct 2026 21:10:28 +0100 Subject: [PATCH 3/4] Keep mock_proofs in the serialized proof engine config `--dump-config` round-trips the client config through JSON, so a skipped `mock_proofs` field read back as false. Serialize it and default it to false when absent. --- beacon_node/proof_engine/src/config.rs | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/beacon_node/proof_engine/src/config.rs b/beacon_node/proof_engine/src/config.rs index 77267e7f267..d6848c86021 100644 --- a/beacon_node/proof_engine/src/config.rs +++ b/beacon_node/proof_engine/src/config.rs @@ -50,7 +50,6 @@ pub struct ProofEngineConfig { execution_proofs: Vec, /// Accept zkboost mock proofs of the configured proof types in place of the ERE verifiers. /// Devnets only. - #[serde(skip)] mock_proofs: bool, /// Ready-built engine returned by [`Self::build_engine`] instead of the configured /// verifiers. Never serialized and only present with the `test-utils` feature, so tests can @@ -197,10 +196,17 @@ impl<'de> Deserialize<'de> for ProofEngineConfig { #[derive(Deserialize)] struct Config { execution_proofs: Vec, + #[serde(default)] + mock_proofs: bool, } let config = Config::deserialize(deserializer)?; - Self::new(config.execution_proofs).map_err(D::Error::custom) + let proof_engine = Self::new(config.execution_proofs).map_err(D::Error::custom)?; + Ok(if config.mock_proofs { + proof_engine.with_mock_proofs() + } else { + proof_engine + }) } } @@ -257,6 +263,24 @@ mod tests { ); } + #[test] + fn mock_proofs_survive_a_json_round_trip() { + let config = ProofEngineConfig::default().with_mock_proofs(); + let json = serde_json::to_string(&config).expect("serialize configuration"); + + assert_eq!( + json.parse::() + .expect("deserialize configuration"), + config + ); + assert!( + !format!(r#"{{"execution_proofs":[{{"proof_type":5,"program_vk":"0x{DEFAULT_RETH_SP1_PROGRAM_VK}"}}]}}"#) + .parse::() + .expect("mock_proofs defaults to false") + .mock_proofs() + ); + } + #[test] fn rejects_invalid_json_fields() { for json in [ From f65c054f265df68f9a7df2c215352a5a8f5dc47a Mon Sep 17 00:00:00 2001 From: frisitano Date: Thu, 22 Oct 2026 11:24:52 +0800 Subject: [PATCH 4/4] Name the mock proof engine for the proof format, not zkboost The mock proof format comes from zkboost's mock zkVMs, but any prover may send it. Keep one reference to zkboost's MockProof as the layout's source, and name the API, flag help, warning and tests after mock proofs. --- beacon_node/proof_engine/src/config.rs | 12 +++--- beacon_node/proof_engine/src/test_utils.rs | 47 ++++++++++------------ beacon_node/src/cli.rs | 4 +- beacon_node/src/config.rs | 2 +- 4 files changed, 30 insertions(+), 35 deletions(-) diff --git a/beacon_node/proof_engine/src/config.rs b/beacon_node/proof_engine/src/config.rs index d6848c86021..c7c47ba67ca 100644 --- a/beacon_node/proof_engine/src/config.rs +++ b/beacon_node/proof_engine/src/config.rs @@ -48,7 +48,7 @@ const DEFAULT_ZESU_ZISK_PROGRAM_VK: &str = #[derive(Debug, Clone, Serialize)] pub struct ProofEngineConfig { execution_proofs: Vec, - /// Accept zkboost mock proofs of the configured proof types in place of the ERE verifiers. + /// Accept mock proofs of the configured proof types in place of the ERE verifiers. /// Devnets only. mock_proofs: bool, /// Ready-built engine returned by [`Self::build_engine`] instead of the configured @@ -105,14 +105,14 @@ impl ProofEngineConfig { &self.execution_proofs } - /// Accept the mock proofs of zkboost's mock zkVMs, for devnets without a prover. See - /// [`MockProofEngine::zkboost`]. + /// Accept mock proofs, for devnets without a real prover. See + /// [`MockProofEngine::accepting_mock_proofs`]. pub fn with_mock_proofs(mut self) -> Self { self.mock_proofs = true; self } - /// Whether zkboost mock proofs are accepted in place of the ERE verifiers. + /// Whether mock proofs are accepted in place of the ERE verifiers. pub fn mock_proofs(&self) -> bool { self.mock_proofs } @@ -125,7 +125,7 @@ impl ProofEngineConfig { } /// Build the proof engine this configuration describes: an injected test engine if one was - /// supplied, the zkboost mock-proof engine in mock mode, otherwise the ERE verifiers, which + /// supplied, the mock-proof engine in mock mode, otherwise the ERE verifiers, which /// need the `ere-verifier` feature. pub fn build_engine(&self) -> Result { #[cfg(feature = "test-utils")] @@ -134,7 +134,7 @@ impl ProofEngineConfig { } if self.mock_proofs { - return Ok(ProofEngine::new(MockProofEngine::zkboost( + return Ok(ProofEngine::new(MockProofEngine::accepting_mock_proofs( self.execution_proofs.iter().map(|config| config.proof_type), ))); } diff --git a/beacon_node/proof_engine/src/test_utils.rs b/beacon_node/proof_engine/src/test_utils.rs index 6c80b831d9e..694fd2755cd 100644 --- a/beacon_node/proof_engine/src/test_utils.rs +++ b/beacon_node/proof_engine/src/test_utils.rs @@ -11,7 +11,7 @@ use types::execution::{ExecutionProof, ProofType}; #[derive(Debug, Default)] pub struct MockProofEngine { valid_proof_data: HashSet>, - /// Proof types whose zkboost mock proofs are accepted. `None` accepts no mock proof. + /// Proof types whose mock proofs are accepted. `None` accepts no mock proof. mock_proof_types: Option>, } @@ -24,13 +24,11 @@ impl MockProofEngine { } } - /// Construct a mock that accepts the mock proofs of zkboost's mock zkVMs for `proof_types`. + /// Construct a mock that accepts mock proofs, as [`MockProof`], for `proof_types`. /// - /// A zkboost mock proof is the SSZ container - /// `MockProof { public_values: List[byte], proof: List[byte] }` whose `proof` is random bytes. - /// It is valid when its public values are the serialized public input of the proof, the bytes - /// a real guest commits; `proof` is not checked. - pub fn zkboost(proof_types: impl IntoIterator) -> Self { + /// A mock proof is valid when its public values are the serialized public input of the proof, + /// the bytes a real guest commits; `proof` is not checked. + pub fn accepting_mock_proofs(proof_types: impl IntoIterator) -> Self { Self { valid_proof_data: HashSet::new(), mock_proof_types: Some(proof_types.into_iter().collect()), @@ -38,8 +36,9 @@ impl MockProofEngine { } } -/// The `proof_data` of a zkboost mock proof, as `MockProof` in eth-act/zkboost -/// (`crates/types/src/execution_proof.rs`). The `proof_data` list bounds both lists. +/// The `proof_data` of a mock proof, sent by provers without a zkVM in place of a real proof. +/// The layout is that of `MockProof` in eth-act/zkboost (`crates/types/src/execution_proof.rs`). +/// The `proof_data` list bounds both lists. #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] pub struct MockProof { /// The public values that the guest commits, as a verifier of a real proof returns them. @@ -110,7 +109,7 @@ mod tests { ); } - fn zkboost_mock_proof(public_values: &[u8], proof: &[u8]) -> Vec { + fn mock_proof(public_values: &[u8], proof: &[u8]) -> Vec { MockProof { public_values: public_values.to_vec(), proof: proof.to_vec(), @@ -121,7 +120,7 @@ mod tests { /// The encoding matches the layout zkboost tests: the offsets of both lists, then the public /// values and the proof. #[test] - fn mock_proof_layout_matches_zkboost() { + fn mock_proof_layout() { let mock_proof = MockProof { public_values: vec![0xaa; 3], proof: vec![0xbb; 2], @@ -138,8 +137,10 @@ mod tests { } #[test] - fn zkboost_mock_proofs_verify_their_public_values() { - let proof_engine = ProofEngine::new(MockProofEngine::zkboost([ProofType::EthrexSP1])); + fn mock_proofs_verify_their_public_values() { + let proof_engine = ProofEngine::new(MockProofEngine::accepting_mock_proofs([ + ProofType::EthrexSP1, + ])); let proof = |proof_type, proof_data| { ExecutionProof::new( ProofData::new(proof_data).expect("proof data within bound"), @@ -158,7 +159,7 @@ mod tests { assert_eq!( verify( ProofType::EthrexSP1, - zkboost_mock_proof(&public_values, &[0xbb; 64]) + mock_proof(&public_values, &[0xbb; 64]) ) .expect("mock verification succeeds"), ProofVerificationOutcome::Valid @@ -167,8 +168,8 @@ mod tests { let mut other_public_values = public_values.clone(); other_public_values[0] ^= 1; for proof_data in [ - zkboost_mock_proof(&other_public_values, &[0xbb; 64]), - zkboost_mock_proof(&public_values[..public_values.len() - 1], &[0xbb; 64]), + mock_proof(&other_public_values, &[0xbb; 64]), + mock_proof(&public_values[..public_values.len() - 1], &[0xbb; 64]), vec![0xff; 64], ] { assert_eq!( @@ -178,16 +179,13 @@ mod tests { } assert!(matches!( - verify( - ProofType::RethSP1, - zkboost_mock_proof(&public_values, &[0xbb; 64]) - ), + verify(ProofType::RethSP1, mock_proof(&public_values, &[0xbb; 64])), Err(ProofEngineError::UnconfiguredProofType(ProofType::RethSP1)) )); } #[test] - fn exact_proof_data_mock_rejects_zkboost_mock_proofs() { + fn exact_proof_data_mock_rejects_mock_proofs() { let proof = ExecutionProof::new( ProofData::new(vec![1]).expect("proof data within bound"), ProofType::RethSP1, @@ -195,11 +193,8 @@ mod tests { 1, ); let mock_proof = ExecutionProof::new( - ProofData::new(zkboost_mock_proof( - &proof.public_input.as_ssz_bytes(), - &[0xbb; 64], - )) - .expect("proof data within bound"), + ProofData::new(mock_proof(&proof.public_input.as_ssz_bytes(), &[0xbb; 64])) + .expect("proof data within bound"), ProofType::RethSP1, Hash256::default(), 1, diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index 247b012b9e5..0c311c326cd 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -865,8 +865,8 @@ pub fn cli_app() -> Command { .long("proof-engine-mock-proofs") .action(ArgAction::SetTrue) .help_heading(FLAG_HEADER) - .help("DEVNETS ONLY. Accept the mock proofs of zkboost's mock zkVMs (an SSZ \ - MockProof) for the proof types of --proof-engine, in place of verifying \ + .help("DEVNETS ONLY. Accept mock proofs (an SSZ MockProof { public_values, \ + proof }) for the proof types of --proof-engine, in place of verifying \ them with ERE. A mock proof is valid when its public values match the \ proof's public input; its proof bytes are not checked.") .requires("proof-engine") diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 23087846b73..b925c5ff2d1 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -309,7 +309,7 @@ pub fn get_config( .map_err(|e| format!("Invalid --proof-engine `{path}`: {e}"))? }); if cli_args.get_flag("proof-engine-mock-proofs") { - warn!("Accepting zkboost mock execution proofs without verifying them. Devnets only"); + warn!("Accepting mock execution proofs without verifying them. Devnets only"); client_config.proof_engine = client_config .proof_engine .take()