From ca7a8d00fe48cd3fb559f505dc17a57ee86cf261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erce=20Can=20Bekt=C3=BCre?= <47954181+ercecan@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:20:14 +0300 Subject: [PATCH] Implement guest side verification (#1402) --- .../actions/validate-bitcoin-da/action.yml | 6 +- .github/workflows/checks.yml | 4 +- .github/workflows/release.yml | 4 +- Cargo.lock | 13 ++-- bin/citrea/Cargo.toml | 2 +- bin/citrea/provers/risc0/Cargo.toml | 7 +- .../Cargo.lock | 2 +- .../Cargo.toml | 2 +- .../src/bin/batch_proof_bitcoin.rs} | 0 .../Cargo.lock | 2 +- .../Cargo.toml | 2 +- .../src/bin/batch_proof_mock.rs} | 0 bin/citrea/provers/risc0/build.rs | 24 +++--- .../Cargo.lock | 3 +- .../Cargo.toml | 2 +- .../src/bin/light_client_proof_bitcoin.rs} | 7 +- .../Cargo.lock | 3 +- .../Cargo.toml | 2 +- .../src/bin/light_client_proof_mock.rs} | 7 +- bin/citrea/src/rollup/bitcoin.rs | 8 +- bin/citrea/src/rollup/mock.rs | 8 +- bin/citrea/tests/test_helpers/mod.rs | 5 +- crates/batch-prover/src/proving.rs | 24 +++--- crates/batch-prover/tests/prover_tests.rs | 14 ++-- crates/bitcoin-da/src/service.rs | 10 +-- crates/common/src/config.rs | 7 -- crates/fullnode/src/da_block_handler.rs | 28 ++----- crates/fullnode/src/runner.rs | 6 -- .../tests/runner_initialization_tests.rs | 1 - crates/light-client-prover/Cargo.toml | 1 + crates/light-client-prover/src/circuit.rs | 19 ++++- .../src/da_block_handler.rs | 42 ++++++++-- crates/light-client-prover/src/input.rs | 2 + crates/prover-services/src/parallel/mod.rs | 30 +++---- crates/prover-services/src/parallel/prover.rs | 65 +++++++++++++--- crates/risc0-bonsai/src/host.rs | 78 ++++++++++++------- .../adapters/mock-da/src/service.rs | 48 ++++++------ .../adapters/mock-zkvm/src/lib.rs | 68 +++++++++------- .../adapters/risc0/src/guest/mod.rs | 23 +++--- .../sovereign-sdk/adapters/risc0/src/lib.rs | 12 +++ .../full-node/db/sov-db/src/schema/types.rs | 16 +--- .../sov-stf-runner/src/prover_service/mod.rs | 15 +++- crates/sovereign-sdk/fuzz/Cargo.lock | 11 +++ .../rollup-interface/src/node/rpc/mod.rs | 11 +-- .../src/state_machine/zk/mod.rs | 22 ++++-- 45 files changed, 395 insertions(+), 271 deletions(-) rename bin/citrea/provers/risc0/{batch-prover-bitcoin => batch-proof-bitcoin}/Cargo.lock (99%) rename bin/citrea/provers/risc0/{batch-prover-bitcoin => batch-proof-bitcoin}/Cargo.toml (98%) rename bin/citrea/provers/risc0/{batch-prover-bitcoin/src/bin/batch_prover_bitcoin.rs => batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs} (100%) rename bin/citrea/provers/risc0/{batch-prover-mock => batch-proof-mock}/Cargo.lock (99%) rename bin/citrea/provers/risc0/{batch-prover-mock => batch-proof-mock}/Cargo.toml (98%) rename bin/citrea/provers/risc0/{batch-prover-mock/src/bin/batch_prover_mock.rs => batch-proof-mock/src/bin/batch_proof_mock.rs} (100%) rename bin/citrea/provers/risc0/{light-client-prover-bitcoin => light-client-proof-bitcoin}/Cargo.lock (99%) rename bin/citrea/provers/risc0/{light-client-prover-bitcoin => light-client-proof-bitcoin}/Cargo.toml (97%) rename bin/citrea/provers/risc0/{light-client-prover-bitcoin/src/bin/light_client_prover_bitcoin.rs => light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs} (70%) rename bin/citrea/provers/risc0/{light-client-prover-mock => light-client-proof-mock}/Cargo.lock (99%) rename bin/citrea/provers/risc0/{light-client-prover-mock => light-client-proof-mock}/Cargo.toml (97%) rename bin/citrea/provers/risc0/{light-client-prover-mock/src/bin/light_client_prover_mock.rs => light-client-proof-mock/src/bin/light_client_proof_mock.rs} (53%) diff --git a/.github/actions/validate-bitcoin-da/action.yml b/.github/actions/validate-bitcoin-da/action.yml index 4cfc2f59e..4dd09c57d 100644 --- a/.github/actions/validate-bitcoin-da/action.yml +++ b/.github/actions/validate-bitcoin-da/action.yml @@ -91,15 +91,15 @@ runs: if: inputs.action == 'check_binary' shell: bash run: | - RESULT=$(grep -R "BATCH_PROVER_BITCOIN_ID" target/ || echo "Grep failed") + RESULT=$(grep -R "BATCH_PROOF_BITCOIN_ID" target/ || echo "Grep failed") EXPECTED_BITCOIN_DA_ID=$(echo "${{ env.EXPECTED_BITCOIN_DA_ID }}" | tr -d '\n\r') if echo "$RESULT" | grep -q "$EXPECTED_BITCOIN_DA_ID"; then echo "Check passed successfully." - echo "Expected: BATCH_PROVER_BITCOIN_ID ${{ env.EXPECTED_BITCOIN_DA_ID }} " + echo "Expected: BATCH_PROOF_BITCOIN_ID ${{ env.EXPECTED_BITCOIN_DA_ID }} " echo "Actual: $RESULT" else - echo "Check failed. Expected: BATCH_PROVER_BITCOIN_ID ${{ env.EXPECTED_BITCOIN_DA_ID }} " + echo "Check failed. Expected: BATCH_PROOF_BITCOIN_ID ${{ env.EXPECTED_BITCOIN_DA_ID }} " echo "Actual: $RESULT" exit 1 fi diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 40e491ea3..ca29a8b5e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -207,6 +207,7 @@ jobs: env: RUST_BACKTRACE: 1 TEST_BITCOIN_DOCKER: 1 + RISC0_DEV_MODE: 1 # This is needed to generate mock proofs and verify them SHORT_PREFIX: 1 CITREA_E2E_TEST_BINARY: ${{ github.workspace }}/target/debug/citrea - name: Upload coverage @@ -374,6 +375,7 @@ jobs: RUST_BACKTRACE: 1 BONSAI_API_URL: ${{ secrets.BONSAI_API_URL }} # TODO: remove this once we don't use the client on tests BONSAI_API_KEY: ${{ secrets.BONSAI_API_KEY }} # TODO: remove this once we don't use the client on tests + RISC0_DEV_MODE: 1 # This is needed to generate mock proofs and verify them TEST_BITCOIN_DOCKER: 1 SHORT_PREFIX: 1 CITREA_E2E_TEST_BINARY: ${{ github.workspace }}/target/debug/citrea @@ -453,7 +455,7 @@ jobs: name: citrea-build path: target/release - - name: Check BATCH_PROVER_BITCOIN_ID + - name: Check BATCH_PROOF_BITCOIN_ID id: check-id uses: ./.github/actions/validate-bitcoin-da with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ddfe88a8..9e2980e04 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,7 +44,7 @@ jobs: run: | cargo build --release - - name: Check BATCH_PROVER_BITCOIN_ID + - name: Check BATCH_PROOF_BITCOIN_ID id: check-id uses: ./.github/actions/validate-bitcoin-da with: @@ -81,7 +81,7 @@ jobs: source $HOME/.cargo/env cargo build --release - - name: Check BATCH_PROVER_BITCOIN_ID + - name: Check BATCH_PROOF_BITCOIN_ID id: check-id uses: ./.github/actions/validate-bitcoin-da with: diff --git a/Cargo.lock b/Cargo.lock index 924ad34ce..878dfaf82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1810,7 +1810,7 @@ dependencies = [ [[package]] name = "citrea-e2e" version = "0.1.0" -source = "git+https://github.com/chainwayxyz/citrea-e2e?rev=bdbd991#bdbd99119dc3e0f865c9364adbd54e95908490e0" +source = "git+https://github.com/chainwayxyz/citrea-e2e?rev=bd8ece7#bd8ece76b5e57bef6efa55fcdd758c0f59e05833" dependencies = [ "anyhow", "async-trait", @@ -1823,8 +1823,8 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sov-ledger-rpc 0.5.0-rc.1 (git+https://github.com/chainwayxyz/citrea?rev=82bf52d)", - "sov-rollup-interface 0.5.0-rc.1 (git+https://github.com/chainwayxyz/citrea?rev=82bf52d)", + "sov-ledger-rpc 0.5.0-rc.1 (git+https://github.com/chainwayxyz/citrea?rev=ab6b5dd)", + "sov-rollup-interface 0.5.0-rc.1 (git+https://github.com/chainwayxyz/citrea?rev=ab6b5dd)", "tempfile", "tokio", "toml", @@ -1922,6 +1922,7 @@ name = "citrea-light-client-prover" version = "0.5.0-rc.1" dependencies = [ "anyhow", + "bincode", "borsh", "citrea-common", "hex", @@ -7557,11 +7558,11 @@ dependencies = [ [[package]] name = "sov-ledger-rpc" version = "0.5.0-rc.1" -source = "git+https://github.com/chainwayxyz/citrea?rev=82bf52d#82bf52d067c1886d92c55c141c8fc062b1931164" +source = "git+https://github.com/chainwayxyz/citrea?rev=ab6b5dd#ab6b5ddf0645f5b61486259845f1bdf62a22ded3" dependencies = [ "jsonrpsee", "serde", - "sov-rollup-interface 0.5.0-rc.1 (git+https://github.com/chainwayxyz/citrea?rev=82bf52d)", + "sov-rollup-interface 0.5.0-rc.1 (git+https://github.com/chainwayxyz/citrea?rev=ab6b5dd)", ] [[package]] @@ -7778,7 +7779,7 @@ dependencies = [ [[package]] name = "sov-rollup-interface" version = "0.5.0-rc.1" -source = "git+https://github.com/chainwayxyz/citrea?rev=82bf52d#82bf52d067c1886d92c55c141c8fc062b1931164" +source = "git+https://github.com/chainwayxyz/citrea?rev=ab6b5dd#ab6b5ddf0645f5b61486259845f1bdf62a22ded3" dependencies = [ "anyhow", "async-trait", diff --git a/bin/citrea/Cargo.toml b/bin/citrea/Cargo.toml index 21b7b82c8..91c1686f5 100644 --- a/bin/citrea/Cargo.toml +++ b/bin/citrea/Cargo.toml @@ -89,7 +89,7 @@ rustc_version_runtime = { workspace = true } # bitcoin-e2e dependencies bitcoin.workspace = true bitcoincore-rpc.workspace = true -citrea-e2e = { git = "https://github.com/chainwayxyz/citrea-e2e", rev = "bdbd991" } +citrea-e2e = { git = "https://github.com/chainwayxyz/citrea-e2e", rev = "bd8ece7" } [build-dependencies] sp1-helper = { version = "3.0.0", default-features = false } diff --git a/bin/citrea/provers/risc0/Cargo.toml b/bin/citrea/provers/risc0/Cargo.toml index 5084a12c9..4bd1365e3 100644 --- a/bin/citrea/provers/risc0/Cargo.toml +++ b/bin/citrea/provers/risc0/Cargo.toml @@ -10,7 +10,12 @@ resolver = "2" risc0-build = { workspace = true } [package.metadata.risc0] -methods = ["batch-prover-bitcoin", "batch-prover-mock", "light-client-prover-bitcoin", "light-client-prover-mock"] +methods = [ + "batch-proof-bitcoin", + "batch-proof-mock", + "light-client-proof-bitcoin", + "light-client-proof-mock", +] [features] bench = [] diff --git a/bin/citrea/provers/risc0/batch-prover-bitcoin/Cargo.lock b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock similarity index 99% rename from bin/citrea/provers/risc0/batch-prover-bitcoin/Cargo.lock rename to bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock index 65a4cf4ee..254629b37 100644 --- a/bin/citrea/provers/risc0/batch-prover-bitcoin/Cargo.lock +++ b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock @@ -560,7 +560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "batch-prover-bitcoin" +name = "batch-proof-bitcoin" version = "0.5.0-rc.1" dependencies = [ "anyhow", diff --git a/bin/citrea/provers/risc0/batch-prover-bitcoin/Cargo.toml b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml similarity index 98% rename from bin/citrea/provers/risc0/batch-prover-bitcoin/Cargo.toml rename to bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml index efe3eafab..ff42e4de1 100644 --- a/bin/citrea/provers/risc0/batch-prover-bitcoin/Cargo.toml +++ b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "batch-prover-bitcoin" +name = "batch-proof-bitcoin" version = "0.5.0-rc.1" edition = "2021" resolver = "2" diff --git a/bin/citrea/provers/risc0/batch-prover-bitcoin/src/bin/batch_prover_bitcoin.rs b/bin/citrea/provers/risc0/batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs similarity index 100% rename from bin/citrea/provers/risc0/batch-prover-bitcoin/src/bin/batch_prover_bitcoin.rs rename to bin/citrea/provers/risc0/batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs diff --git a/bin/citrea/provers/risc0/batch-prover-mock/Cargo.lock b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock similarity index 99% rename from bin/citrea/provers/risc0/batch-prover-mock/Cargo.lock rename to bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock index c93fa7e5a..bbdfc2034 100644 --- a/bin/citrea/provers/risc0/batch-prover-mock/Cargo.lock +++ b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock @@ -550,7 +550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] -name = "batch-prover-mock" +name = "batch-proof-mock" version = "0.5.0-rc.1" dependencies = [ "anyhow", diff --git a/bin/citrea/provers/risc0/batch-prover-mock/Cargo.toml b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml similarity index 98% rename from bin/citrea/provers/risc0/batch-prover-mock/Cargo.toml rename to bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml index f8bf43468..5ae0aeb10 100644 --- a/bin/citrea/provers/risc0/batch-prover-mock/Cargo.toml +++ b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "batch-prover-mock" +name = "batch-proof-mock" version = "0.5.0-rc.1" edition = "2021" resolver = "2" diff --git a/bin/citrea/provers/risc0/batch-prover-mock/src/bin/batch_prover_mock.rs b/bin/citrea/provers/risc0/batch-proof-mock/src/bin/batch_proof_mock.rs similarity index 100% rename from bin/citrea/provers/risc0/batch-prover-mock/src/bin/batch_prover_mock.rs rename to bin/citrea/provers/risc0/batch-proof-mock/src/bin/batch_proof_mock.rs diff --git a/bin/citrea/provers/risc0/build.rs b/bin/citrea/provers/risc0/build.rs index 4dc8fba8a..7a2c12101 100644 --- a/bin/citrea/provers/risc0/build.rs +++ b/bin/citrea/provers/risc0/build.rs @@ -16,14 +16,14 @@ fn main() { let methods_path = out_dir.join("methods.rs"); let elf = r#" - pub const BATCH_PROVER_BITCOIN_ELF: &[u8] = &[]; - pub const BATCH_PROVER_BITCOIN_ID: [u32; 8] = [0u32; 8]; - pub const BATCH_PROVER_MOCK_ELF: &[u8] = &[]; - pub const BATCH_PROVER_MOCK_ID: [u32; 8] = [0u32; 8]; - pub const LIGHT_CLIENT_PROVER_BITCOIN_ELF: &[u8] = &[]; - pub const LIGHT_CLIENT_PROVER_BITCOIN_ID: [u32; 8] = [0u32; 8]; - pub const LIGHT_CLIENT_PROVER_MOCK_ELF: &[u8] = &[]; - pub const LIGHT_CLIENT_PROVER_MOCK_ID: [u32; 8] = [0u32; 8]; + pub const BATCH_PROOF_BITCOIN_ELF: &[u8] = &[]; + pub const BATCH_PROOF_BITCOIN_ID: [u32; 8] = [0u32; 8]; + pub const BATCH_PROOF_MOCK_ELF: &[u8] = &[]; + pub const BATCH_PROOF_MOCK_ID: [u32; 8] = [0u32; 8]; + pub const LIGHT_CLIENT_PROOF_BITCOIN_ELF: &[u8] = &[]; + pub const LIGHT_CLIENT_PROOF_BITCOIN_ID: [u32; 8] = [0u32; 8]; + pub const LIGHT_CLIENT_PROOF_MOCK_ELF: &[u8] = &[]; + pub const LIGHT_CLIENT_PROOF_MOCK_ID: [u32; 8] = [0u32; 8]; "#; return std::fs::write(methods_path, elf).expect("Failed to write mock rollup elf"); @@ -62,28 +62,28 @@ fn get_guest_options() -> HashMap<&'static str, risc0_build::GuestOptions> { }; guest_pkg_to_options.insert( - "batch-prover-bitcoin", + "batch-proof-bitcoin", GuestOptions { features: features.clone(), use_docker: use_docker.clone(), }, ); guest_pkg_to_options.insert( - "batch-prover-mock", + "batch-proof-mock", GuestOptions { features: features.clone(), use_docker: use_docker.clone(), }, ); guest_pkg_to_options.insert( - "light-client-prover-bitcoin", + "light-client-proof-bitcoin", GuestOptions { features: features.clone(), use_docker: use_docker.clone(), }, ); guest_pkg_to_options.insert( - "light-client-prover-mock", + "light-client-proof-mock", GuestOptions { features: features.clone(), use_docker: use_docker.clone(), diff --git a/bin/citrea/provers/risc0/light-client-prover-bitcoin/Cargo.lock b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock similarity index 99% rename from bin/citrea/provers/risc0/light-client-prover-bitcoin/Cargo.lock rename to bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock index 9e4834424..e79d771f1 100644 --- a/bin/citrea/provers/risc0/light-client-prover-bitcoin/Cargo.lock +++ b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock @@ -802,6 +802,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" name = "citrea-light-client-prover" version = "0.5.0-rc.1" dependencies = [ + "bincode", "borsh", "hex", "sov-modules-api", @@ -1619,7 +1620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "light-client-prover-bitcoin" +name = "light-client-proof-bitcoin" version = "0.5.0-rc.1" dependencies = [ "anyhow", diff --git a/bin/citrea/provers/risc0/light-client-prover-bitcoin/Cargo.toml b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml similarity index 97% rename from bin/citrea/provers/risc0/light-client-prover-bitcoin/Cargo.toml rename to bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml index 9214f11d7..78e343af4 100644 --- a/bin/citrea/provers/risc0/light-client-prover-bitcoin/Cargo.toml +++ b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "light-client-prover-bitcoin" +name = "light-client-proof-bitcoin" version = "0.5.0-rc.1" edition = "2021" resolver = "2" diff --git a/bin/citrea/provers/risc0/light-client-prover-bitcoin/src/bin/light_client_prover_bitcoin.rs b/bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs similarity index 70% rename from bin/citrea/provers/risc0/light-client-prover-bitcoin/src/bin/light_client_prover_bitcoin.rs rename to bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs index fbe5d5ea0..0444d024d 100644 --- a/bin/citrea/provers/risc0/light-client-prover-bitcoin/src/bin/light_client_prover_bitcoin.rs +++ b/bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs @@ -1,8 +1,7 @@ #![no_main] -use bitcoin_da::spec::{BitcoinSpec, RollupParams}; +use bitcoin_da::spec::RollupParams; use bitcoin_da::verifier::BitcoinVerifier; use citrea_light_client_prover::circuit::run_circuit; -use citrea_light_client_prover::input::LightClientCircuitInput; use citrea_primitives::{REVEAL_BATCH_PROOF_PREFIX, REVEAL_LIGHT_CLIENT_PREFIX}; use sov_risc0_adapter::guest::Risc0Guest; use sov_rollup_interface::da::DaVerifier; @@ -13,14 +12,12 @@ risc0_zkvm::guest::entry!(main); pub fn main() { let guest = Risc0Guest::new(); - let input: LightClientCircuitInput = guest.read_from_host(); - let da_verifier = BitcoinVerifier::new(RollupParams { reveal_batch_prover_prefix: REVEAL_BATCH_PROOF_PREFIX.to_vec(), reveal_light_client_prefix: REVEAL_LIGHT_CLIENT_PREFIX.to_vec(), }); - let output = run_circuit::(input, da_verifier).unwrap(); + let output = run_circuit::(da_verifier, &guest).unwrap(); guest.commit(&output); } diff --git a/bin/citrea/provers/risc0/light-client-prover-mock/Cargo.lock b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock similarity index 99% rename from bin/citrea/provers/risc0/light-client-prover-mock/Cargo.lock rename to bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock index ddb7d5ba3..666a5720b 100644 --- a/bin/citrea/provers/risc0/light-client-prover-mock/Cargo.lock +++ b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock @@ -713,6 +713,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" name = "citrea-light-client-prover" version = "0.5.0-rc.1" dependencies = [ + "bincode", "borsh", "hex", "sov-modules-api", @@ -1426,7 +1427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "light-client-prover-mock" +name = "light-client-proof-mock" version = "0.5.0-rc.1" dependencies = [ "anyhow", diff --git a/bin/citrea/provers/risc0/light-client-prover-mock/Cargo.toml b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml similarity index 97% rename from bin/citrea/provers/risc0/light-client-prover-mock/Cargo.toml rename to bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml index f216360d0..9778bba36 100644 --- a/bin/citrea/provers/risc0/light-client-prover-mock/Cargo.toml +++ b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "light-client-prover-mock" +name = "light-client-proof-mock" version = "0.5.0-rc.1" edition = "2021" resolver = "2" diff --git a/bin/citrea/provers/risc0/light-client-prover-mock/src/bin/light_client_prover_mock.rs b/bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs similarity index 53% rename from bin/citrea/provers/risc0/light-client-prover-mock/src/bin/light_client_prover_mock.rs rename to bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs index b551d6d34..48f7b626b 100644 --- a/bin/citrea/provers/risc0/light-client-prover-mock/src/bin/light_client_prover_mock.rs +++ b/bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs @@ -1,7 +1,6 @@ #![no_main] use citrea_light_client_prover::circuit::run_circuit; -use citrea_light_client_prover::input::LightClientCircuitInput; -use sov_mock_da::{MockDaSpec, MockDaVerifier}; +use sov_mock_da::MockDaVerifier; use sov_risc0_adapter::guest::Risc0Guest; use sov_rollup_interface::zk::ZkvmGuest; @@ -10,11 +9,9 @@ risc0_zkvm::guest::entry!(main); pub fn main() { let guest = Risc0Guest::new(); - let input: LightClientCircuitInput = guest.read_from_host(); - let da_verifier = MockDaVerifier {}; - let output = run_circuit::(input, da_verifier).unwrap(); + let output = run_circuit::(da_verifier, &guest).unwrap(); guest.commit(&output); } diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index 260d53d16..7084a911a 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -101,7 +101,7 @@ impl RollupBlueprint for BitcoinRollup { let mut map = HashMap::new(); map.insert( SpecId::Genesis, - Digest::new(citrea_risc0::BATCH_PROVER_BITCOIN_ID), + Digest::new(citrea_risc0::BATCH_PROOF_BITCOIN_ID), ); // let (_, vk) = citrea_sp1::host::CLIENT.setup(include_bytes!("../../provers/sp1/batch-prover-bitcoin/elf/zkvm-elf")); // map.insert(SpecId::Genesis, vk); @@ -110,7 +110,7 @@ impl RollupBlueprint for BitcoinRollup { #[instrument(level = "trace", skip(self), ret)] fn get_light_client_prover_code_commitment(&self) -> ::CodeCommitment { - Digest::new(citrea_risc0::LIGHT_CLIENT_PROVER_BITCOIN_ID) + Digest::new(citrea_risc0::LIGHT_CLIENT_PROOF_BITCOIN_ID) } #[instrument(level = "trace", skip_all, err)] @@ -173,7 +173,7 @@ impl RollupBlueprint for BitcoinRollup { ledger_db: LedgerDB, ) -> Self::ProverService { let vm = Risc0BonsaiHost::new( - citrea_risc0::BATCH_PROVER_BITCOIN_ELF, + citrea_risc0::BATCH_PROOF_BITCOIN_ELF, std::env::var("BONSAI_API_URL").unwrap_or("".to_string()), std::env::var("BONSAI_API_KEY").unwrap_or("".to_string()), ledger_db.clone(), @@ -211,7 +211,7 @@ impl RollupBlueprint for BitcoinRollup { ledger_db: LedgerDB, ) -> Self::ProverService { let vm = Risc0BonsaiHost::new( - citrea_risc0::LIGHT_CLIENT_PROVER_BITCOIN_ELF, + citrea_risc0::LIGHT_CLIENT_PROOF_BITCOIN_ELF, std::env::var("BONSAI_API_URL").unwrap_or("".to_string()), std::env::var("BONSAI_API_KEY").unwrap_or("".to_string()), ledger_db.clone(), diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index 294e2846b..f056108d1 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -91,13 +91,13 @@ impl RollupBlueprint for MockDemoRollup { let mut map = HashMap::new(); map.insert( SpecId::Genesis, - Digest::new(citrea_risc0::BATCH_PROVER_MOCK_ID), + Digest::new(citrea_risc0::BATCH_PROOF_MOCK_ID), ); map } fn get_light_client_prover_code_commitment(&self) -> ::CodeCommitment { - Digest::new(citrea_risc0::LIGHT_CLIENT_PROVER_MOCK_ID) + Digest::new(citrea_risc0::LIGHT_CLIENT_PROOF_MOCK_ID) } async fn create_da_service( @@ -119,7 +119,7 @@ impl RollupBlueprint for MockDemoRollup { ledger_db: LedgerDB, ) -> Self::ProverService { let vm = Risc0BonsaiHost::new( - citrea_risc0::BATCH_PROVER_MOCK_ELF, + citrea_risc0::BATCH_PROOF_MOCK_ELF, std::env::var("BONSAI_API_URL").unwrap_or("".to_string()), std::env::var("BONSAI_API_KEY").unwrap_or("".to_string()), ledger_db.clone(), @@ -147,7 +147,7 @@ impl RollupBlueprint for MockDemoRollup { ledger_db: LedgerDB, ) -> Self::ProverService { let vm = Risc0BonsaiHost::new( - citrea_risc0::LIGHT_CLIENT_PROVER_MOCK_ELF, + citrea_risc0::LIGHT_CLIENT_PROOF_MOCK_ELF, std::env::var("BONSAI_API_URL").unwrap_or("".to_string()), std::env::var("BONSAI_API_KEY").unwrap_or("".to_string()), ledger_db.clone(), diff --git a/bin/citrea/tests/test_helpers/mod.rs b/bin/citrea/tests/test_helpers/mod.rs index b527f1ecd..3476d7bcf 100644 --- a/bin/citrea/tests/test_helpers/mod.rs +++ b/bin/citrea/tests/test_helpers/mod.rs @@ -44,6 +44,10 @@ pub async fn start_rollup( ) { // create rollup config default creator function and use them here for the configs + // We enable risc0 dev mode in tests because the provers in dev mode generate fake receipts that can be verified if the verifier is also in dev mode + // Fake receipts are receipts without the proof, they only include the journal, which makes them suitable for testing and development + std::env::set_var("RISC0_DEV_MODE", "1"); + let mock_demo_rollup = MockDemoRollup {}; if sequencer_config.is_some() && rollup_prover_config.is_some() { @@ -162,7 +166,6 @@ pub fn create_default_rollup_config( | NodeMode::LightClientProver(socket_addr) => Some(RunnerConfig { include_tx_body, sequencer_client_url: format!("http://localhost:{}", socket_addr.port()), - accept_public_input_as_proven: Some(true), sync_blocks_count: 10, pruning_config: None, }), diff --git a/crates/batch-prover/src/proving.rs b/crates/batch-prover/src/proving.rs index 70447388d..d47616930 100644 --- a/crates/batch-prover/src/proving.rs +++ b/crates/batch-prover/src/proving.rs @@ -18,7 +18,7 @@ use sov_rollup_interface::services::da::DaService; use sov_rollup_interface::zk::{Proof, StateTransitionData, ZkvmHost}; use sov_stf_runner::ProverService; use tokio::sync::Mutex; -use tracing::{info, warn}; +use tracing::info; use crate::da_block_handler::{ break_sequencer_commitments_into_groups, get_state_transition_data_from_commitments, @@ -270,7 +270,7 @@ where Witness: BorshSerialize, { prover_service - .submit_witness(borsh::to_vec(&transition_data)?, hash.clone()) + .submit_input(borsh::to_vec(&transition_data)?, hash.clone()) .await; prover_service.prove(hash.clone()).await?; @@ -333,19 +333,13 @@ where let transition_data = Vm::extract_output::<::Spec, StateRoot>(&proof) .expect("Proof should be deserializable"); - match &proof { - Proof::PublicInput(_) => { - warn!("Proof is public input, skipping"); - } - Proof::Full(data) => { - info!("Verifying proof!"); - let code_commitment = code_commitments_by_spec - .get(&transition_data.last_active_spec_id) - .expect("Proof public input must contain valid spec id"); - Vm::verify(data, code_commitment) - .map_err(|err| anyhow!("Failed to verify proof: {:?}. Skipping it...", err))?; - } - } + info!("Verifying proof!"); + + let code_commitment = code_commitments_by_spec + .get(&transition_data.last_active_spec_id) + .expect("Proof public input must contain valid spec id"); + Vm::verify(proof.as_slice(), code_commitment) + .map_err(|err| anyhow!("Failed to verify proof: {:?}. Skipping it...", err))?; info!("transition data: {:?}", transition_data); diff --git a/crates/batch-prover/tests/prover_tests.rs b/crates/batch-prover/tests/prover_tests.rs index 9359c2238..e7d2532e0 100644 --- a/crates/batch-prover/tests/prover_tests.rs +++ b/crates/batch-prover/tests/prover_tests.rs @@ -29,7 +29,7 @@ async fn test_successful_prover_execution() -> Result<(), ProverServiceError> { let header_hash = MockHash::from([0; 32]); prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) @@ -70,7 +70,7 @@ async fn test_prover_status_busy() -> Result<(), anyhow::Error> { // Saturate the prover. for header_hash in header_hashes.clone() { prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) @@ -87,7 +87,7 @@ async fn test_prover_status_busy() -> Result<(), anyhow::Error> { { let header_hash = MockHash::from([0; 32]); prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) @@ -119,7 +119,7 @@ async fn test_prover_status_busy() -> Result<(), anyhow::Error> { { let header_hash = MockHash::from([(num_worker_threads + 1) as u8; 32]); prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) @@ -151,7 +151,7 @@ async fn test_multiple_witness_submissions() -> Result<(), anyhow::Error> { let header_hash = MockHash::from([0; 32]); let submission_status = prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) @@ -163,7 +163,7 @@ async fn test_multiple_witness_submissions() -> Result<(), anyhow::Error> { ); let submission_status = prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) @@ -180,7 +180,7 @@ async fn test_generate_multiple_proofs_for_the_same_witness() -> Result<(), anyh let header_hash = MockHash::from([0; 32]); prover_service - .submit_witness( + .submit_input( borsh::to_vec(&make_transition_data(header_hash)).unwrap(), header_hash, ) diff --git a/crates/bitcoin-da/src/service.rs b/crates/bitcoin-da/src/service.rs index 1cddc5c34..3968da262 100644 --- a/crates/bitcoin-da/src/service.rs +++ b/crates/bitcoin-da/src/service.rs @@ -1365,7 +1365,7 @@ mod tests { /// A test we use to generate some data for the other tests async fn send_transaction() { use sov_rollup_interface::da::DaData; - use sov_rollup_interface::zk::Proof; + let da_service = get_service().await; da_service @@ -1394,7 +1394,7 @@ mod tests { let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service - .send_transaction(DaData::ZKProof(Proof::Full(blob))) + .send_transaction(DaData::ZKProof(blob)) .await .expect("Failed to send transaction"); @@ -1406,7 +1406,7 @@ mod tests { let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service - .send_transaction(DaData::ZKProof(Proof::Full(blob))) + .send_transaction(DaData::ZKProof(blob)) .await .expect("Failed to send transaction"); @@ -1425,7 +1425,7 @@ mod tests { let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service - .send_transaction(DaData::ZKProof(Proof::Full(blob))) + .send_transaction(DaData::ZKProof(blob)) .await .expect("Failed to send transaction"); @@ -1453,7 +1453,7 @@ mod tests { let blob = (0..size).map(|_| rand::random::()).collect::>(); da_service - .send_transaction(DaData::ZKProof(Proof::Full(blob))) + .send_transaction(DaData::ZKProof(blob)) .await .expect("Failed to send transaction"); diff --git a/crates/common/src/config.rs b/crates/common/src/config.rs index 153debc8d..84be06932 100644 --- a/crates/common/src/config.rs +++ b/crates/common/src/config.rs @@ -33,8 +33,6 @@ pub struct RunnerConfig { pub sequencer_client_url: String, /// Saves sequencer soft confirmations if set to true pub include_tx_body: bool, - /// Only true for tests - pub accept_public_input_as_proven: Option, /// Number of blocks to request during sync #[serde(default = "default_sync_blocks_count")] pub sync_blocks_count: u64, @@ -46,9 +44,6 @@ impl FromEnv for RunnerConfig { Ok(Self { sequencer_client_url: std::env::var("SEQUENCER_CLIENT_URL")?, include_tx_body: std::env::var("INCLUDE_TX_BODY")?.parse()?, - accept_public_input_as_proven: std::env::var("ACCEPT_PUBLIC_INPUT_AS_PROVEN") - .ok() - .and_then(|val| val.parse().ok()), sync_blocks_count: std::env::var("SYNC_BLOCKS_COUNT") .ok() .and_then(|val| val.parse().ok()) @@ -446,7 +441,6 @@ mod tests { runner: Some(RunnerConfig { sequencer_client_url: "http://0.0.0.0:12346".to_owned(), include_tx_body: true, - accept_public_input_as_proven: None, sync_blocks_count: 10, pruning_config: None, }), @@ -648,7 +642,6 @@ mod tests { runner: Some(RunnerConfig { sequencer_client_url: "http://0.0.0.0:12346".to_string(), include_tx_body: true, - accept_public_input_as_proven: None, sync_blocks_count: default_sync_blocks_count(), pruning_config: Some(PruningConfig { distance: 1000 }), }), diff --git a/crates/fullnode/src/da_block_handler.rs b/crates/fullnode/src/da_block_handler.rs index 84ebc6bbf..c34e69876 100644 --- a/crates/fullnode/src/da_block_handler.rs +++ b/crates/fullnode/src/da_block_handler.rs @@ -49,7 +49,6 @@ where sequencer_da_pub_key: Vec, prover_da_pub_key: Vec, code_commitments_by_spec: HashMap, - accept_public_input_as_proven: bool, l1_block_cache: Arc>>, pending_l1_blocks: VecDeque<::FilteredBlock>, _context: PhantomData, @@ -78,7 +77,6 @@ where sequencer_da_pub_key: Vec, prover_da_pub_key: Vec, code_commitments_by_spec: HashMap, - accept_public_input_as_proven: bool, l1_block_cache: Arc>>, ) -> Self { Self { @@ -88,7 +86,6 @@ where sequencer_da_pub_key, prover_da_pub_key, code_commitments_by_spec, - accept_public_input_as_proven, l1_block_cache, pending_l1_blocks: VecDeque::new(), _context: PhantomData, @@ -309,25 +306,12 @@ where ).into()); } - match &proof { - Proof::Full(data) => { - let code_commitment = self - .code_commitments_by_spec - .get(&state_transition.last_active_spec_id) - .expect("Proof public input must contain valid spec id"); - Vm::verify(data, code_commitment) - .map_err(|err| anyhow!("Failed to verify proof: {:?}. Skipping it...", err))?; - } - Proof::PublicInput(_) => { - if !self.accept_public_input_as_proven { - return Err(anyhow!( - "Found public input in da block number: {}, Skipping to next proof..", - l1_block.header().height(), - ) - .into()); - } - } - } + let code_commitment = self + .code_commitments_by_spec + .get(&state_transition.last_active_spec_id) + .expect("Proof public input must contain valid spec id"); + Vm::verify(proof.as_slice(), code_commitment) + .map_err(|err| anyhow!("Failed to verify proof: {:?}. Skipping it...", err))?; let stored_state_transition = StoredStateTransition { initial_state_root: state_transition.initial_state_root.as_ref().to_vec(), diff --git a/crates/fullnode/src/runner.rs b/crates/fullnode/src/runner.rs index 93269bfbc..c25d583e5 100644 --- a/crates/fullnode/src/runner.rs +++ b/crates/fullnode/src/runner.rs @@ -63,7 +63,6 @@ where phantom: std::marker::PhantomData, include_tx_body: bool, code_commitments_by_spec: HashMap, - accept_public_input_as_proven: bool, l1_block_cache: Arc>>, sync_blocks_count: u64, fork_manager: ForkManager, @@ -147,9 +146,6 @@ where phantom: std::marker::PhantomData, include_tx_body: runner_config.include_tx_body, code_commitments_by_spec, - accept_public_input_as_proven: runner_config - .accept_public_input_as_proven - .unwrap_or(false), sync_blocks_count: runner_config.sync_blocks_count, l1_block_cache: Arc::new(Mutex::new(L1BlockCache::new())), fork_manager, @@ -342,7 +338,6 @@ where let sequencer_da_pub_key = self.sequencer_da_pub_key.clone(); let prover_da_pub_key = self.prover_da_pub_key.clone(); let code_commitments_by_spec = self.code_commitments_by_spec.clone(); - let accept_public_input_as_proven = self.accept_public_input_as_proven; let l1_block_cache = self.l1_block_cache.clone(); self.task_manager @@ -354,7 +349,6 @@ where sequencer_da_pub_key, prover_da_pub_key, code_commitments_by_spec, - accept_public_input_as_proven, l1_block_cache.clone(), ); l1_block_handler diff --git a/crates/fullnode/tests/runner_initialization_tests.rs b/crates/fullnode/tests/runner_initialization_tests.rs index c20be79af..b122ab646 100644 --- a/crates/fullnode/tests/runner_initialization_tests.rs +++ b/crates/fullnode/tests/runner_initialization_tests.rs @@ -83,7 +83,6 @@ fn initialize_runner( runner: Some(RunnerConfig { sequencer_client_url: "http://127.0.0.1:4444".to_string(), include_tx_body: true, - accept_public_input_as_proven: None, sync_blocks_count: 10, pruning_config: None, }), diff --git a/crates/light-client-prover/Cargo.toml b/crates/light-client-prover/Cargo.toml index ce6e474d8..0c2ab9b98 100644 --- a/crates/light-client-prover/Cargo.toml +++ b/crates/light-client-prover/Cargo.toml @@ -22,6 +22,7 @@ sov-stf-runner = { path = "../sovereign-sdk/full-node/sov-stf-runner", optional # 3rd-party deps anyhow = { workspace = true, optional = true } +bincode = { workspace = true } borsh = { workspace = true } hex = { workspace = true } jsonrpsee = { workspace = true, optional = true } diff --git a/crates/light-client-prover/src/circuit.rs b/crates/light-client-prover/src/circuit.rs index 8fc4d830d..bfdbddf82 100644 --- a/crates/light-client-prover/src/circuit.rs +++ b/crates/light-client-prover/src/circuit.rs @@ -1,6 +1,7 @@ use borsh::BorshDeserialize; use sov_modules_api::BlobReaderTrait; use sov_rollup_interface::da::{DaDataLightClient, DaVerifier}; +use sov_rollup_interface::zk::ZkvmGuest; use crate::input::LightClientCircuitInput; use crate::output::LightClientCircuitOutput; @@ -10,11 +11,13 @@ pub enum LightClientVerificationError { DaTxsCouldntBeVerified, } -pub fn run_circuit( - input: LightClientCircuitInput, +pub fn run_circuit( da_verifier: DaV, + guest: &G, ) -> Result { - // Veriy data from da + let input: LightClientCircuitInput = guest.read_from_host(); + + // Verify data from da let _validity_condition = da_verifier .verify_relevant_tx_list_light_client( &input.da_block_header, @@ -42,9 +45,17 @@ pub fn run_circuit( } } + let batch_proof_journals = input.batch_proof_journals; + let batch_proof_method_id = input.batch_proof_method_id; + // TODO: Test for multiple assumptions to see if the env::verify function does automatic matching between the journal and the assumption or do we need to verify them in order? + // https://github.com/chainwayxyz/citrea/issues/1401 + for journal in batch_proof_journals { + G::verify(&journal, &batch_proof_method_id.into()).unwrap(); + } + // do what you want with proofs // complete proof has raw bytes inside - // to extract *and* verift the proof you need to use the zkguest + // to extract *and* verify the proof you need to use the zk guest // can be passed from the guest code to this function Ok(LightClientCircuitOutput { diff --git a/crates/light-client-prover/src/da_block_handler.rs b/crates/light-client-prover/src/da_block_handler.rs index d36b9f25b..3a26aaa67 100644 --- a/crates/light-client-prover/src/da_block_handler.rs +++ b/crates/light-client-prover/src/da_block_handler.rs @@ -34,7 +34,7 @@ where ledger_db: DB, da_service: Arc, batch_prover_da_pub_key: Vec, - _batch_proof_code_commitments_by_spec: HashMap, + batch_proof_code_commitments_by_spec: HashMap, _light_client_proof_code_commitment: Vm::CodeCommitment, l1_block_cache: Arc>>, queued_l1_blocks: VecDeque<::FilteredBlock>, @@ -63,7 +63,7 @@ where ledger_db, da_service, batch_prover_da_pub_key, - _batch_proof_code_commitments_by_spec: batch_proof_code_commitments_by_spec, + batch_proof_code_commitments_by_spec, _light_client_proof_code_commitment: light_client_proof_code_commitment, l1_block_cache: Arc::new(Mutex::new(L1BlockCache::new())), queued_l1_blocks: VecDeque::new(), @@ -149,10 +149,33 @@ where // Do any kind of ordering etc. on batch proofs here // If you do so, don't forget to do the same inside zk + let batch_proof_method_id = self + .batch_proof_code_commitments_by_spec + .get(&SpecId::Genesis) + .expect("Batch proof code commitment not found"); + + let mut assumptions = vec![]; + let mut journals = vec![]; + for batch_proof in batch_proofs { + if let DaDataLightClient::Complete(proof) = batch_proof { + match Vm::verify(proof.as_slice(), batch_proof_method_id) { + Ok(output) => { + assumptions.push(proof); + journals.push(output); + } + Err(e) => { + tracing::error!("Failed to verify batch proof: {:?}", e); + continue; + } + } + } + } - let circuit_input = self.create_circuit_input(da_data, l1_block).await; + let circuit_input = self + .create_circuit_input(da_data, l1_block, batch_proof_method_id, journals) + .await; - let circuit_output = self.prove(circuit_input).await?; + let circuit_output = self.prove(circuit_input, assumptions).await?; tracing::info!( "Generated proof for L1 block: {l1_height} output={:?}", @@ -197,6 +220,8 @@ where &self, da_data: Vec<<::Spec as DaSpec>::BlobTransaction>, l1_block: &Da::FilteredBlock, + batch_prover_code_commitment: &::CodeCommitment, + journals: Vec>, ) -> LightClientCircuitInput { let (inclusion_proof, completeness_proof) = self .da_service @@ -209,18 +234,25 @@ where completeness_proof, da_block_header: l1_block.header().clone(), batch_prover_da_pub_key: self.batch_prover_da_pub_key.clone(), + batch_proof_method_id: batch_prover_code_commitment.clone().into(), + batch_proof_journals: journals, } } async fn prove( &self, circuit_input: LightClientCircuitInput<::Spec>, + assumptions: Vec>, ) -> Result { let da_slot_hash = circuit_input.da_block_header.hash(); let prover_service = self.prover_service.as_ref(); prover_service - .submit_witness(borsh::to_vec(&circuit_input)?, da_slot_hash.clone()) + .submit_input(borsh::to_vec(&circuit_input)?, da_slot_hash.clone()) + .await; + + prover_service + .submit_assumptions(assumptions, da_slot_hash.clone()) .await; prover_service.prove(da_slot_hash.clone()).await?; diff --git a/crates/light-client-prover/src/input.rs b/crates/light-client-prover/src/input.rs index 491236ef6..5be36f8ca 100644 --- a/crates/light-client-prover/src/input.rs +++ b/crates/light-client-prover/src/input.rs @@ -9,4 +9,6 @@ pub struct LightClientCircuitInput { pub da_block_header: Da::BlockHeader, pub batch_prover_da_pub_key: Vec, + pub batch_proof_method_id: [u32; 8], + pub batch_proof_journals: Vec>, } diff --git a/crates/prover-services/src/parallel/mod.rs b/crates/prover-services/src/parallel/mod.rs index fc9dd12fd..9e62f52c7 100644 --- a/crates/prover-services/src/parallel/mod.rs +++ b/crates/prover-services/src/parallel/mod.rs @@ -6,7 +6,7 @@ use borsh::BorshDeserialize; use citrea_stf::verifier::StateTransitionVerifier; use parking_lot::Mutex; use prover::Prover; -use risc0_zkvm::{Journal, Receipt}; +use risc0_zkvm::Receipt; use sov_db::ledger_db::{LedgerDB, ProvingServiceLedgerOps}; use sov_rollup_interface::da::{DaData, DaSpec}; use sov_rollup_interface::services::da::DaService; @@ -27,7 +27,7 @@ where Vm: ZkvmHost, V: StateTransitionFunction + Send + Sync, { - vm: Vm, + pub vm: Vm, prover_config: Arc>>, zk_storage: V::PreState, @@ -123,12 +123,21 @@ where { type DaService = Da; - async fn submit_witness( + async fn submit_assumptions( + &self, + assumptions: Vec>, + da_slot_hash: ::SlotHash, + ) { + self.prover_state + .submit_assumptions(assumptions, da_slot_hash); + } + + async fn submit_input( &self, input: Vec, da_slot_hash: ::SlotHash, ) -> WitnessSubmissionStatus { - self.prover_state.submit_witness(input, da_slot_hash) + self.prover_state.submit_input(input, da_slot_hash) } async fn prove( @@ -155,16 +164,9 @@ where // TODO: maybe extract this to Vm? // Extract journal - let journal = match proof { - Proof::PublicInput(journal) => { - let journal: Journal = bincode::deserialize(&journal)?; - journal - } - Proof::Full(data) => { - let receipt: Receipt = bincode::deserialize(&data)?; - receipt.journal - } - }; + + let receipt: Receipt = bincode::deserialize(&proof)?; + let journal = receipt.journal; self.ledger_db.clear_pending_proving_sessions()?; diff --git a/crates/prover-services/src/parallel/prover.rs b/crates/prover-services/src/parallel/prover.rs index 4a74ceca4..fd956babc 100644 --- a/crates/prover-services/src/parallel/prover.rs +++ b/crates/prover-services/src/parallel/prover.rs @@ -13,8 +13,11 @@ use sov_stf_runner::{ProofProcessingStatus, ProverServiceError, WitnessSubmissio use crate::ProofGenConfig; +pub(crate) type Assumption = Vec; +pub(crate) type Input = Vec; + pub(crate) enum ProverStatus { - WitnessSubmitted(Vec), + WitnessSubmitted((Input, Vec)), ProvingInProgress, #[allow(dead_code)] Proved(Proof), @@ -93,21 +96,61 @@ where }) } - pub(crate) fn submit_witness( + pub(crate) fn submit_assumptions( + &self, + assumptions: Vec>, + da_slot_hash: ::SlotHash, + ) { + let header_hash = da_slot_hash; + let mut prover_state = self.prover_state.lock(); + + // If entry doesn't exist, create a default empty entry + let entry = prover_state.prover_status.entry(header_hash); + + match entry { + Entry::Occupied(mut occupied) => { + // Only update if it's a WitnessSubmitted variant + if let ProverStatus::WitnessSubmitted((_, existing_assumptions)) = + occupied.get_mut() + { + *existing_assumptions = assumptions; + } + } + Entry::Vacant(vacant) => { + // Create a new entry with empty input and the given assumptions + vacant.insert(ProverStatus::WitnessSubmitted((Vec::new(), assumptions))); + } + } + } + + pub(crate) fn submit_input( &self, input: Vec, da_slot_hash: ::SlotHash, ) -> WitnessSubmissionStatus { let header_hash = da_slot_hash; - let data = ProverStatus::WitnessSubmitted(input); - let mut prover_state = self.prover_state.lock(); + + // If entry doesn't exist, create a default empty entry let entry = prover_state.prover_status.entry(header_hash); match entry { - Entry::Occupied(_) => WitnessSubmissionStatus::WitnessExist, + Entry::Occupied(mut occupied) => { + // Check if the entry is a WitnessSubmitted variant with an empty input + if let ProverStatus::WitnessSubmitted((existing_input, _)) = occupied.get_mut() { + if existing_input.is_empty() { + *existing_input = input; + WitnessSubmissionStatus::SubmittedForProving + } else { + WitnessSubmissionStatus::WitnessExist + } + } else { + WitnessSubmissionStatus::WitnessExist + } + } Entry::Vacant(v) => { - v.insert(data); + // Create a new entry with the given input and empty assumptions + v.insert(ProverStatus::WitnessSubmitted((input, vec![]))); WitnessSubmissionStatus::SubmittedForProving } } @@ -133,12 +176,16 @@ where .ok_or_else(|| anyhow::anyhow!("Missing witness for block: {:?}", block_header_hash))?; match prover_status { - ProverStatus::WitnessSubmitted(state_transition_data) => { + ProverStatus::WitnessSubmitted((state_transition_data, assumptions)) => { let start_prover = prover_state.inc_task_count_if_not_busy(self.num_threads); // Initiate a new proving job only if the prover is not busy. if start_prover { prover_state.set_to_proving(block_header_hash.clone()); vm.add_hint(state_transition_data); + for assumption in assumptions { + vm.add_assumption(assumption.clone()); + tracing::warn!("added assumption: {:?}", assumption); + } self.pool.spawn(move || { tracing::debug_span!("guest_execution").in_scope(|| { @@ -212,10 +259,10 @@ where { let mut config = config.lock(); match config.deref_mut() { - ProofGenConfig::Skip => Ok(Proof::PublicInput(Vec::default())), + ProofGenConfig::Skip => Ok(Vec::default()), ProofGenConfig::Simulate(ref mut verifier) => verifier .run_sequencer_commitments_in_da_slot(vm.simulate_with_hints(), zk_storage) - .map(|_| Proof::PublicInput(Vec::default())) + .map(|_| Vec::default()) .map_err(|e| anyhow::anyhow!("Guest execution must succeed but failed with {:?}", e)), ProofGenConfig::Execute => vm.run(false), ProofGenConfig::Prover => vm.run(true), diff --git a/crates/risc0-bonsai/src/host.rs b/crates/risc0-bonsai/src/host.rs index b6dc45a03..0a5c5d18a 100644 --- a/crates/risc0-bonsai/src/host.rs +++ b/crates/risc0-bonsai/src/host.rs @@ -9,7 +9,8 @@ use bonsai_sdk::blocking::Client; use borsh::{BorshDeserialize, BorshSerialize}; use risc0_zkvm::sha::Digest; use risc0_zkvm::{ - compute_image_id, ExecutorEnvBuilder, ExecutorImpl, Journal, LocalProver, Prover, Receipt, + compute_image_id, AssumptionReceipt, ExecutorEnvBuilder, InnerReceipt, LocalProver, ProveInfo, + Prover, Receipt, }; use sov_db::ledger_db::{LedgerDB, ProvingServiceLedgerOps}; use sov_risc0_adapter::guest::Risc0Guest; @@ -85,6 +86,7 @@ macro_rules! retry_backoff_bonsai { pub struct Risc0BonsaiHost<'a> { elf: &'a [u8], env: Vec, + assumptions: Vec, image_id: Digest, client: Option, ledger_db: LedgerDB, @@ -124,6 +126,7 @@ impl<'a> Risc0BonsaiHost<'a> { Self { elf, env: Default::default(), + assumptions: vec![], image_id, client, ledger_db, @@ -262,9 +265,7 @@ impl<'a> Risc0BonsaiHost<'a> { tracing::info!("Snark proof!: {snark_receipt:?}"); - // now we convert the snark_receipt to a full receipt - - return Ok(Proof::Full(snark_receipt_buf)); + return Ok(snark_receipt_buf); } _ => { return Err(anyhow!( @@ -293,30 +294,54 @@ impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { Risc0Guest::with_hints(std::mem::take(&mut self.env)) } + fn add_assumption(&mut self, receipt_buf: Vec) { + let receipt: Receipt = bincode::deserialize(&receipt_buf).expect("Receipt should be valid"); + self.assumptions.push(receipt.into()); + } + /// Only with_proof = true is supported. /// Proofs are created on the Bonsai API. fn run(&mut self, with_proof: bool) -> Result { let proof = match (self.client.as_ref(), with_proof) { // Local execution. If mode is Execute, we always do local execution. (_, false) => { - let env = add_benchmarking_callbacks(ExecutorEnvBuilder::default()) - .write_slice(&self.env) - .build() - .unwrap(); - let mut executor = ExecutorImpl::from_elf(env, self.elf)?; + // Set dev mode to true as this is used for tests + std::env::set_var("RISC0_DEV_MODE", "1"); + let mut env = add_benchmarking_callbacks(ExecutorEnvBuilder::default()); + for assumption in self.assumptions.iter() { + env.add_assumption(assumption.clone()); + } + + tracing::debug!("{:?} assumptions added to the env", self.assumptions.len()); + + let env = env.write_slice(&self.env).build().unwrap(); + + let prover = LocalProver::new("citrea-test-prover"); + + tracing::info!("Starting risc0 proving"); + let ProveInfo { receipt, stats } = prover.prove(env, self.elf)?; + + // Because the dev mode is set, the proof will generate a fake receipt which does not include the proof + // It only includes the journal + assert!(matches!(receipt.inner, InnerReceipt::Fake(_))); + + tracing::info!("Execution Stats: {:?}", stats); - let session = executor.run()?; - let data = - bincode::serialize(&session.journal.expect("Journal shouldn't be empty"))?; + receipt.verify(self.image_id)?; + + tracing::info!("Verified the fake receipt"); - Ok(Proof::PublicInput(data)) + let serialized_receipt = bincode::serialize(&receipt)?; + + Ok(serialized_receipt) } // Local proving (None, true) => { - let env = add_benchmarking_callbacks(ExecutorEnvBuilder::default()) - .write_slice(&self.env) - .build() - .unwrap(); + let mut env = add_benchmarking_callbacks(ExecutorEnvBuilder::default()); + for assumption in self.assumptions.iter() { + env.add_assumption(assumption.clone()); + } + let env = env.write_slice(&self.env).build().unwrap(); let prover = LocalProver::new("citrea"); let receipt = prover.prove(env, self.elf)?.receipt; @@ -329,7 +354,7 @@ impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { let serialized_receipt = bincode::serialize(&receipt)?; - Ok(Proof::Full(serialized_receipt)) + Ok(serialized_receipt) } // Bonsai proving (Some(client), true) => { @@ -340,6 +365,7 @@ impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { let client_clone = client.clone(); // Start a Bonsai session let session = thread::spawn(move || { + // TODO: Get necessary assumptions by giving the receipt uuids retry_backoff_bonsai!(client_clone.create_session( image_id.clone(), input_id.clone(), @@ -388,22 +414,18 @@ impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { // Cleanup env self.env.clear(); + // Cleanup assumptions + self.assumptions.clear(); + Ok(proof) } fn extract_output( proof: &Proof, ) -> Result, Self::Error> { - let journal = match proof { - Proof::PublicInput(journal) => { - let journal: Journal = bincode::deserialize(journal)?; - journal - } - Proof::Full(data) => { - let receipt: Receipt = bincode::deserialize(data)?; - receipt.journal - } - }; + let receipt: Receipt = bincode::deserialize(proof)?; + let journal = receipt.journal; + Ok(BorshDeserialize::try_from_slice(&journal.bytes)?) } diff --git a/crates/sovereign-sdk/adapters/mock-da/src/service.rs b/crates/sovereign-sdk/adapters/mock-da/src/service.rs index 21f1e4cd6..d22a2ae0f 100644 --- a/crates/sovereign-sdk/adapters/mock-da/src/service.rs +++ b/crates/sovereign-sdk/adapters/mock-da/src/service.rs @@ -153,8 +153,7 @@ impl MockDaService { blocks.prune_above(height); for blob in blobs { - use sov_rollup_interface::zk::Proof; - let da_data = DaData::ZKProof(Proof::Full(blob)); + let da_data = DaData::ZKProof(blob); let blob = borsh::to_vec(&da_data).unwrap(); self.add_blob(&blocks, blob, Default::default()).unwrap(); } @@ -560,7 +559,6 @@ fn block_hash( #[cfg(test)] mod tests { use sov_rollup_interface::da::{BlobReaderTrait, BlockHeaderTrait}; - use sov_rollup_interface::zk::Proof; use tokio::task::JoinHandle; use tokio_stream::StreamExt; @@ -640,7 +638,7 @@ mod tests { get_finalized_headers_collector(&mut da, number_of_finalized_blocks).await; for i in 0..num_blocks { - let proof = Proof::Full(vec![i as u8; i + 1]); + let proof = vec![i as u8; i + 1]; let published_blob = DaData::ZKProof(proof.clone()); let height = (i + 1) as u64; @@ -692,7 +690,7 @@ mod tests { for (i, blob) in blobs.iter().enumerate() { let height = (i + 1) as u64; // Send transaction should pass - da.send_transaction(DaData::ZKProof(Proof::Full(blob.to_owned()))) + da.send_transaction(DaData::ZKProof(blob.to_owned())) .await .unwrap(); let last_finalized_block_response = da.get_last_finalized_block_header().await; @@ -720,7 +718,7 @@ mod tests { let last_finalized_header = da.get_last_finalized_block_header().await.unwrap(); assert_eq!(expected_finalized_height, last_finalized_header.height()); - let proof = Proof::Full(blob); + let proof = blob; let retrieved_data = fetched_block.blobs[0].full_data(); let retrieved_data = DaDataLightClient::try_from_slice(retrieved_data).unwrap(); let DaDataLightClient::Complete(retrieved_proof) = retrieved_data else { @@ -778,13 +776,13 @@ mod tests { // 1 -> 2 -> 3 - da.send_transaction(DaData::ZKProof(Proof::Full(vec![1, 2, 3, 4]))) + da.send_transaction(DaData::ZKProof(vec![1, 2, 3, 4])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![4, 5, 6, 7]))) + da.send_transaction(DaData::ZKProof(vec![4, 5, 6, 7])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![8, 9, 0, 1]))) + da.send_transaction(DaData::ZKProof(vec![8, 9, 0, 1])) .await .unwrap(); @@ -835,19 +833,19 @@ mod tests { // \ -> 3.2 -> 4.2 // 1 - da.send_transaction(DaData::ZKProof(Proof::Full(vec![1, 2, 3, 4]))) + da.send_transaction(DaData::ZKProof(vec![1, 2, 3, 4])) .await .unwrap(); // 2 - da.send_transaction(DaData::ZKProof(Proof::Full(vec![4, 5, 6, 7]))) + da.send_transaction(DaData::ZKProof(vec![4, 5, 6, 7])) .await .unwrap(); // 3.1 - da.send_transaction(DaData::ZKProof(Proof::Full(vec![8, 9, 0, 1]))) + da.send_transaction(DaData::ZKProof(vec![8, 9, 0, 1])) .await .unwrap(); // 4.1 - da.send_transaction(DaData::ZKProof(Proof::Full(vec![2, 3, 4, 5]))) + da.send_transaction(DaData::ZKProof(vec![2, 3, 4, 5])) .await .unwrap(); @@ -877,16 +875,16 @@ mod tests { // 1 -> 2 -> 3 -> 4 - da.send_transaction(DaData::ZKProof(Proof::Full(vec![1, 2, 3, 4]))) + da.send_transaction(DaData::ZKProof(vec![1, 2, 3, 4])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![4, 5, 6, 7]))) + da.send_transaction(DaData::ZKProof(vec![4, 5, 6, 7])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![8, 9, 0, 1]))) + da.send_transaction(DaData::ZKProof(vec![8, 9, 0, 1])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![2, 3, 4, 5]))) + da.send_transaction(DaData::ZKProof(vec![2, 3, 4, 5])) .await .unwrap(); @@ -946,13 +944,13 @@ mod tests { assert!(has_planned_fork.is_some()); } - da.send_transaction(DaData::ZKProof(Proof::Full(vec![1, 2, 3, 4]))) + da.send_transaction(DaData::ZKProof(vec![1, 2, 3, 4])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![4, 5, 6, 7]))) + da.send_transaction(DaData::ZKProof(vec![4, 5, 6, 7])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![8, 9, 0, 1]))) + da.send_transaction(DaData::ZKProof(vec![8, 9, 0, 1])) .await .unwrap(); @@ -984,19 +982,19 @@ mod tests { PlannedFork::new(4, 2, vec![vec![13, 13, 13, 13], vec![14, 14, 14, 14]]); da.set_planned_fork(planned_fork).await.unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![1, 1, 1, 1]))) + da.send_transaction(DaData::ZKProof(vec![1, 1, 1, 1])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![2, 2, 2, 2]))) + da.send_transaction(DaData::ZKProof(vec![2, 2, 2, 2])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![3, 3, 3, 3]))) + da.send_transaction(DaData::ZKProof(vec![3, 3, 3, 3])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![4, 4, 4, 4]))) + da.send_transaction(DaData::ZKProof(vec![4, 4, 4, 4])) .await .unwrap(); - da.send_transaction(DaData::ZKProof(Proof::Full(vec![5, 5, 5, 5]))) + da.send_transaction(DaData::ZKProof(vec![5, 5, 5, 5])) .await .unwrap(); diff --git a/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs b/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs index ede043078..e685c882a 100644 --- a/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs +++ b/crates/sovereign-sdk/adapters/mock-zkvm/src/lib.rs @@ -22,6 +22,26 @@ impl Matches for MockCodeCommitment { } } +impl From for [u32; 8] { + fn from(val: MockCodeCommitment) -> Self { + let mut output = [0u32; 8]; + for (i, chunk) in val.0.chunks(4).enumerate() { + output[i] = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + } + output + } +} + +impl From<[u32; 8]> for MockCodeCommitment { + fn from(value: [u32; 8]) -> Self { + let mut output = [0u8; 32]; + for (i, &val) in value.iter().enumerate() { + output[i * 4..(i + 1) * 4].copy_from_slice(&val.to_le_bytes()); + } + MockCodeCommitment(output) + } +} + /// A mock proof generated by a zkVM. #[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] pub struct MockProof { @@ -160,43 +180,39 @@ impl sov_rollup_interface::zk::ZkvmHost self.committed_data.push_back(data) } + fn add_assumption(&mut self, _receipt_buf: Vec) { + unimplemented!() + } + fn simulate_with_hints(&mut self) -> Self::Guest { MockZkGuest {} } fn run(&mut self, _with_proof: bool) -> Result { self.worker_thread_notifier.wait(); - let data = self.committed_data.pop_front().unwrap_or_default(); - Ok(sov_rollup_interface::zk::Proof::PublicInput(data)) + Ok(self.committed_data.pop_front().unwrap_or_default()) } fn extract_output( proof: &Proof, ) -> Result, Self::Error> { - match proof { - sov_rollup_interface::zk::Proof::PublicInput(pub_input) => { - let data: ProofInfo = bincode::deserialize(pub_input)?; - let st: StateTransitionData = - BorshDeserialize::deserialize(&mut &*data.hint)?; - - Ok(sov_rollup_interface::zk::StateTransition { - initial_state_root: st.initial_state_root, - final_state_root: st.final_state_root, - initial_batch_hash: st.initial_batch_hash, - validity_condition: data.validity_condition, - state_diff: Default::default(), - da_slot_hash: st.da_block_header_of_commitments.hash(), - sequencer_public_key: vec![], - sequencer_da_public_key: vec![], - sequencer_commitments_range: (0, 0), - last_active_spec_id: SpecId::Genesis, - preproven_commitments: vec![], - }) - } - sov_rollup_interface::zk::Proof::Full(_) => { - panic!("Mock DA doesn't generate real proofs") - } - } + let data: ProofInfo = bincode::deserialize(proof)?; + let st: StateTransitionData = + BorshDeserialize::deserialize(&mut &*data.hint)?; + + Ok(sov_rollup_interface::zk::StateTransition { + initial_state_root: st.initial_state_root, + final_state_root: st.final_state_root, + initial_batch_hash: st.initial_batch_hash, + validity_condition: data.validity_condition, + state_diff: Default::default(), + da_slot_hash: st.da_block_header_of_commitments.hash(), + sequencer_public_key: vec![], + sequencer_da_public_key: vec![], + sequencer_commitments_range: (0, 0), + last_active_spec_id: SpecId::Genesis, + preproven_commitments: vec![], + }) } fn recover_proving_sessions(&self) -> Result, anyhow::Error> { diff --git a/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs b/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs index 1d1e5429f..f52e88d6d 100644 --- a/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs +++ b/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs @@ -4,6 +4,7 @@ //! The host implementation is used for tests only and brings no real value. use borsh::BorshDeserialize; +use risc0_zkvm::guest::env; use sov_rollup_interface::zk::Zkvm; use crate::Risc0MethodId; @@ -20,26 +21,28 @@ pub use native::Risc0Guest; #[cfg(target_os = "zkvm")] pub use zkvm::Risc0Guest; -// Here goes the common implementation: - -// This is a dummy impl because T: ZkvmGuest where T: Zkvm. impl Zkvm for Risc0Guest { type CodeCommitment = Risc0MethodId; type Error = anyhow::Error; fn verify( - _serialized_proof: &[u8], - _code_commitment: &Self::CodeCommitment, + journal: &[u8], + code_commitment: &Self::CodeCommitment, ) -> Result, Self::Error> { - // Implement this method once risc0 supports recursion: issue #633 - todo!("Implement once risc0 supports recursion: https://github.com/Sovereign-Labs/sovereign-sdk/issues/633") + env::verify(code_commitment.0, journal) + .expect("Guest side verification error should be Infallible"); + Ok(journal.to_vec()) } fn verify_and_extract_output( - _serialized_proof: &[u8], - _code_commitment: &Self::CodeCommitment, + journal: &[u8], + code_commitment: &Self::CodeCommitment, ) -> Result, Self::Error> { - todo!() + env::verify(code_commitment.0, journal) + .expect("Guest side verification error should be Infallible"); + Ok(BorshDeserialize::deserialize( + &mut journal.to_vec().as_slice(), + )?) } } diff --git a/crates/sovereign-sdk/adapters/risc0/src/lib.rs b/crates/sovereign-sdk/adapters/risc0/src/lib.rs index b99bf2d2c..48d32cc38 100644 --- a/crates/sovereign-sdk/adapters/risc0/src/lib.rs +++ b/crates/sovereign-sdk/adapters/risc0/src/lib.rs @@ -48,3 +48,15 @@ impl Matches<[u32; 8]> for Risc0MethodId { &self.0 == other } } + +impl From for [u32; 8] { + fn from(val: Risc0MethodId) -> Self { + val.0 + } +} + +impl From<[u32; 8]> for Risc0MethodId { + fn from(value: [u32; 8]) -> Self { + Risc0MethodId(value) + } +} diff --git a/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs b/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs index 37036b24e..3ecd1ba7a 100644 --- a/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs +++ b/crates/sovereign-sdk/full-node/db/sov-db/src/schema/types.rs @@ -5,8 +5,8 @@ use borsh::{BorshDeserialize, BorshSerialize}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use sov_rollup_interface::rpc::{ - HexTx, ProofResponse, ProofRpcResponse, SoftConfirmationResponse, StateTransitionRpcResponse, - TxIdentifier, TxResponse, VerifiedProofResponse, + HexTx, ProofResponse, SoftConfirmationResponse, StateTransitionRpcResponse, TxIdentifier, + TxResponse, VerifiedProofResponse, }; use sov_rollup_interface::soft_confirmation::SignedSoftConfirmation; use sov_rollup_interface::stf::{Event, EventKey, TransactionReceipt}; @@ -88,7 +88,7 @@ impl From for ProofResponse { fn from(value: StoredProof) -> Self { Self { l1_tx_id: value.l1_tx_id, - proof: convert_to_rpc_proof(value.proof), + proof: value.proof, state_transition: StateTransitionRpcResponse::from(value.state_transition), } } @@ -106,7 +106,7 @@ pub struct StoredVerifiedProof { impl From for VerifiedProofResponse { fn from(value: StoredVerifiedProof) -> Self { Self { - proof: convert_to_rpc_proof(value.proof), + proof: value.proof, state_transition: StateTransitionRpcResponse::from(value.state_transition), } } @@ -154,14 +154,6 @@ impl From for StateTransitionRpcResponse { } } -/// Converts proof data to hex encoded rpc response -pub fn convert_to_rpc_proof(stored_proof: Proof) -> ProofRpcResponse { - match stored_proof { - Proof::Full(data) => ProofRpcResponse::Full(data), - Proof::PublicInput(data) => ProofRpcResponse::PublicInput(data), - } -} - /// The on-disk format for a batch. Stores the hash and identifies the range of transactions /// included in the batch. #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize)] diff --git a/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs b/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs index e64525a56..032b595ee 100644 --- a/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs +++ b/crates/sovereign-sdk/full-node/sov-stf-runner/src/prover_service/mod.rs @@ -79,18 +79,25 @@ pub enum ProverServiceError { /// This service is responsible for ZK proof generation. /// The proof generation process involves the following stages: -/// 1. Submitting a witness using the `submit_witness` method to a prover service. +/// 1. Submitting an input witness using the `submit_input` method to a prover service. /// 2. Initiating proof generation with the `prove` method. /// Once the proof is ready, it can be sent to the DA with `send_proof_to_da` method. -/// Currently, the cancellation of proving jobs for submitted witnesses is not supported, +/// Currently, the cancellation of proving jobs for submitted inputs is not supported, /// but this functionality will be added in the future (#1185). #[async_trait] pub trait ProverService { /// Data Availability service. type DaService: DaService; - /// Submit a witness for proving. - async fn submit_witness( + /// Submits assumptions for guest side verification + async fn submit_assumptions( + &self, + assumptions: Vec>, + da_slot_hash: <::Spec as DaSpec>::SlotHash, + ); + + /// Submit an input witness for proving. + async fn submit_input( &self, input: Vec, da_slot_hash: <::Spec as DaSpec>::SlotHash, diff --git a/crates/sovereign-sdk/fuzz/Cargo.lock b/crates/sovereign-sdk/fuzz/Cargo.lock index fd7e21f6b..a10e6fddd 100644 --- a/crates/sovereign-sdk/fuzz/Cargo.lock +++ b/crates/sovereign-sdk/fuzz/Cargo.lock @@ -1363,6 +1363,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.3" @@ -2128,6 +2138,7 @@ dependencies = [ "byteorder", "hex", "jmt", + "num_cpus", "rlimit", "rocksdb", "serde", diff --git a/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs b/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs index 6505b3d22..6a181a0ee 100644 --- a/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs +++ b/crates/sovereign-sdk/rollup-interface/src/node/rpc/mod.rs @@ -240,16 +240,7 @@ pub struct LastVerifiedProofResponse { } /// The ZK proof generated by the [`ZkvmHost::run`] method to be served by rpc. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -#[serde(rename_all = "camelCase")] -pub enum ProofRpcResponse { - /// Only public input was generated. - #[serde(with = "hex::serde")] - PublicInput(Vec), - /// The serialized ZK proof. - #[serde(with = "hex::serde")] - Full(Vec), -} +pub type ProofRpcResponse = Vec; /// The state transition response of ledger proof data rpc #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] diff --git a/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs b/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs index de7a94872..d2eae2ada 100644 --- a/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs +++ b/crates/sovereign-sdk/rollup-interface/src/state_machine/zk/mod.rs @@ -24,13 +24,7 @@ use crate::soft_confirmation::SignedSoftConfirmation; use crate::spec::SpecId; /// The ZK proof generated by the [`ZkvmHost::run`] method. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -pub enum Proof { - /// Only public input was generated. - PublicInput(Vec), - /// The serialized ZK proof. - Full(Vec), -} +pub type Proof = Vec; /// A trait implemented by the prover ("host") of a zkVM program. pub trait ZkvmHost: Zkvm + Clone { @@ -60,13 +54,25 @@ pub trait ZkvmHost: Zkvm + Clone { /// Host recovers pending proving sessions and returns proving results fn recover_proving_sessions(&self) -> Result, anyhow::Error>; + + /// Host adds an assumption to the proving session + /// Assumptions are used for recursive proving + fn add_assumption(&mut self, receipt_buf: Vec); } /// A Zk proof system capable of proving and verifying arbitrary Rust code /// Must support recursive proofs. pub trait Zkvm: Send + Sync { /// A commitment to the zkVM program which is being proven - type CodeCommitment: Clone + Debug + Serialize + DeserializeOwned + Send + Sync + 'static; + type CodeCommitment: Clone + + Debug + + Serialize + + DeserializeOwned + + From<[u32; 8]> + + Into<[u32; 8]> + + Send + + Sync + + 'static; /// The error type which is returned when a proof fails to verify type Error: Debug;