diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 5f5e073ff..2870b7b49 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -374,8 +374,6 @@ jobs: run: make test env: 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 CI_TEST_MODE: 1 diff --git a/Cargo.lock b/Cargo.lock index 564cd6972..0c850f8b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1716,7 +1716,7 @@ dependencies = [ "citrea-fullnode", "citrea-light-client-prover", "citrea-primitives", - "citrea-risc0-bonsai-adapter", + "citrea-risc0-adapter", "citrea-sequencer", "citrea-stf", "clap", @@ -1990,11 +1990,10 @@ dependencies = [ ] [[package]] -name = "citrea-risc0-bonsai-adapter" +name = "citrea-risc0-adapter" version = "0.5.0-rc.1" dependencies = [ "anyhow", - "backoff", "bincode", "bonsai-sdk", "borsh", @@ -2002,7 +2001,6 @@ dependencies = [ "risc0-zkvm", "serde", "sov-db", - "sov-risc0-adapter", "sov-rollup-interface 0.5.0-rc.1", "tracing", ] @@ -7502,25 +7500,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "sov-risc0-adapter" -version = "0.5.0-rc.1" -dependencies = [ - "anyhow", - "bincode", - "borsh", - "bytemuck", - "once_cell", - "parking_lot", - "risc0-circuit-rv32im", - "risc0-zkp", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "sov-rollup-interface 0.5.0-rc.1", - "sov-zk-cycle-utils", -] - [[package]] name = "sov-rollup-interface" version = "0.5.0-rc.1" @@ -7870,18 +7849,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" +checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" +checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 56cbb24f7..fac0088b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,13 @@ members = [ "crates/primitives", "crates/prover-services", "crates/pruning", - "crates/risc0-bonsai", + "crates/risc0", "crates/sequencer", "crates/sequencer-client", "crates/soft-confirmation-rule-enforcer", # "crates/sp1", # Sovereign sdk "crates/sovereign-sdk/rollup-interface", - "crates/sovereign-sdk/adapters/risc0", "crates/sovereign-sdk/adapters/mock-da", "crates/sovereign-sdk/adapters/mock-zkvm", # Full Node diff --git a/Makefile b/Makefile index 7faeab2fb..8c715e971 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ # The release tag of https://github.com/ethereum/tests to use for EF tests EF_TESTS_URL := https://github.com/chainwayxyz/ef-tests/archive/develop.tar.gz EF_TESTS_DIR := crates/evm/ethereum-tests +CITREA_E2E_TEST_BINARY := $(CURDIR)/target/debug/citrea PARALLEL_PROOF_LIMIT := 1 .PHONY: help diff --git a/bin/citrea/Cargo.toml b/bin/citrea/Cargo.toml index 91c1686f5..903fc0c64 100644 --- a/bin/citrea/Cargo.toml +++ b/bin/citrea/Cargo.toml @@ -20,7 +20,7 @@ citrea-fullnode = { path = "../../crates/fullnode" } citrea-light-client-prover = { path = "../../crates/light-client-prover", features = ["native"] } citrea-primitives = { path = "../../crates/primitives" } citrea-risc0 = { package = "risc0", path = "./provers/risc0" } -citrea-risc0-bonsai-adapter = { path = "../../crates/risc0-bonsai", features = ["native"] } +citrea-risc0-adapter = { path = "../../crates/risc0", features = ["native"] } citrea-sequencer = { path = "../../crates/sequencer" } # citrea-sp1 = { path = "../../crates/sp1", features = ["native"] } citrea-stf = { path = "../../crates/citrea-stf", features = ["native"] } diff --git a/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock index 254629b37..521e645b3 100644 --- a/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock +++ b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.lock @@ -566,12 +566,12 @@ dependencies = [ "anyhow", "bitcoin-da", "citrea-primitives", + "citrea-risc0-adapter", "citrea-stf", "risc0-zkvm", "risc0-zkvm-platform", "sov-modules-api", "sov-modules-stf-blueprint", - "sov-risc0-adapter", "sov-rollup-interface", "sov-state", ] @@ -935,6 +935,20 @@ dependencies = [ "sov-rollup-interface", ] +[[package]] +name = "citrea-risc0-adapter" +version = "0.5.0-rc.1" +dependencies = [ + "anyhow", + "bincode", + "borsh", + "hex", + "risc0-zkvm", + "serde", + "sov-rollup-interface", + "tracing", +] + [[package]] name = "citrea-stf" version = "0.5.0-rc.1" @@ -2993,20 +3007,6 @@ dependencies = [ "sov-state", ] -[[package]] -name = "sov-risc0-adapter" -version = "0.5.0-rc.1" -dependencies = [ - "anyhow", - "bincode", - "borsh", - "bytemuck", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "sov-rollup-interface", -] - [[package]] name = "sov-rollup-interface" version = "0.5.0-rc.1" diff --git a/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml index ff42e4de1..4babae5b3 100644 --- a/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml +++ b/bin/citrea/provers/risc0/batch-proof-bitcoin/Cargo.toml @@ -13,10 +13,10 @@ risc0-zkvm-platform = { version = "1.1.2" } anyhow = "1.0.68" bitcoin-da = { path = "../../../../../crates/bitcoin-da", default-features = false } citrea-primitives = { path = "../../../../../crates/primitives" } +citrea-risc0-adapter = { path = "../../../../../crates/risc0" } citrea-stf = { path = "../../../../../crates/citrea-stf" } sov-modules-api = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-api", default-features = false } sov-modules-stf-blueprint = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-stf-blueprint" } -sov-risc0-adapter = { path = "../../../../../crates/sovereign-sdk/adapters/risc0" } sov-rollup-interface = { path = "../../../../../crates/sovereign-sdk/rollup-interface" } sov-state = { path = "../../../../../crates/sovereign-sdk/module-system/sov-state" } diff --git a/bin/citrea/provers/risc0/batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs b/bin/citrea/provers/risc0/batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs index 5f6607650..a0ad18df8 100644 --- a/bin/citrea/provers/risc0/batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs +++ b/bin/citrea/provers/risc0/batch-proof-bitcoin/src/bin/batch_proof_bitcoin.rs @@ -2,11 +2,11 @@ use bitcoin_da::spec::RollupParams; use bitcoin_da::verifier::BitcoinVerifier; use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; +use citrea_risc0_adapter::guest::Risc0Guest; use citrea_stf::runtime::Runtime; use citrea_stf::StfVerifier; use sov_modules_api::default_context::ZkDefaultContext; use sov_modules_stf_blueprint::StfBlueprint; -use sov_risc0_adapter::guest::Risc0Guest; use sov_rollup_interface::da::DaVerifier; use sov_state::ZkStorage; diff --git a/bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock index bbdfc2034..f3e21ea28 100644 --- a/bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock +++ b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.lock @@ -554,13 +554,13 @@ name = "batch-proof-mock" version = "0.5.0-rc.1" dependencies = [ "anyhow", + "citrea-risc0-adapter", "citrea-stf", "risc0-zkvm", "risc0-zkvm-platform", "sov-mock-da", "sov-modules-api", "sov-modules-stf-blueprint", - "sov-risc0-adapter", "sov-state", ] @@ -844,6 +844,20 @@ dependencies = [ "sov-rollup-interface", ] +[[package]] +name = "citrea-risc0-adapter" +version = "0.5.0-rc.1" +dependencies = [ + "anyhow", + "bincode", + "borsh", + "hex", + "risc0-zkvm", + "serde", + "sov-rollup-interface", + "tracing", +] + [[package]] name = "citrea-stf" version = "0.5.0-rc.1" @@ -2796,20 +2810,6 @@ dependencies = [ "sov-state", ] -[[package]] -name = "sov-risc0-adapter" -version = "0.5.0-rc.1" -dependencies = [ - "anyhow", - "bincode", - "borsh", - "bytemuck", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "sov-rollup-interface", -] - [[package]] name = "sov-rollup-interface" version = "0.5.0-rc.1" diff --git a/bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml index 5ae0aeb10..e7c6cc802 100644 --- a/bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml +++ b/bin/citrea/provers/risc0/batch-proof-mock/Cargo.toml @@ -11,11 +11,11 @@ risc0-zkvm = { version = "1.1.2", default-features = false } risc0-zkvm-platform = { version = "1.1.2" } anyhow = "1.0" +citrea-risc0-adapter = { path = "../../../../../crates/risc0" } citrea-stf = { path = "../../../../../crates/citrea-stf" } sov-mock-da = { path = "../../../../../crates/sovereign-sdk/adapters/mock-da", default-features = false } sov-modules-api = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-api", default-features = false } sov-modules-stf-blueprint = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-stf-blueprint" } -sov-risc0-adapter = { path = "../../../../../crates/sovereign-sdk/adapters/risc0" } sov-state = { path = "../../../../../crates/sovereign-sdk/module-system/sov-state" } [patch.crates-io] diff --git a/bin/citrea/provers/risc0/batch-proof-mock/src/bin/batch_proof_mock.rs b/bin/citrea/provers/risc0/batch-proof-mock/src/bin/batch_proof_mock.rs index 27a04c479..eaca37c90 100644 --- a/bin/citrea/provers/risc0/batch-proof-mock/src/bin/batch_proof_mock.rs +++ b/bin/citrea/provers/risc0/batch-proof-mock/src/bin/batch_proof_mock.rs @@ -4,7 +4,7 @@ use citrea_stf::StfVerifier; use sov_mock_da::MockDaVerifier; use sov_modules_api::default_context::ZkDefaultContext; use sov_modules_stf_blueprint::StfBlueprint; -use sov_risc0_adapter::guest::Risc0Guest; +use citrea_risc0_adapter::guest::Risc0Guest; use sov_state::ZkStorage; risc0_zkvm::guest::entry!(main); diff --git a/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock index e79d771f1..dea6ae7b4 100644 --- a/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock +++ b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.lock @@ -820,6 +820,20 @@ dependencies = [ "sov-rollup-interface", ] +[[package]] +name = "citrea-risc0-adapter" +version = "0.5.0-rc.1" +dependencies = [ + "anyhow", + "bincode", + "borsh", + "hex", + "risc0-zkvm", + "serde", + "sov-rollup-interface", + "tracing", +] + [[package]] name = "const-hex" version = "1.13.1" @@ -1627,11 +1641,11 @@ dependencies = [ "bitcoin-da", "citrea-light-client-prover", "citrea-primitives", + "citrea-risc0-adapter", "risc0-zkvm", "risc0-zkvm-platform", "sov-modules-api", "sov-modules-stf-blueprint", - "sov-risc0-adapter", "sov-rollup-interface", "sov-state", ] @@ -2758,20 +2772,6 @@ dependencies = [ "sov-state", ] -[[package]] -name = "sov-risc0-adapter" -version = "0.5.0-rc.1" -dependencies = [ - "anyhow", - "bincode", - "borsh", - "bytemuck", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "sov-rollup-interface", -] - [[package]] name = "sov-rollup-interface" version = "0.5.0-rc.1" diff --git a/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml index 78e343af4..a91c3d374 100644 --- a/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml +++ b/bin/citrea/provers/risc0/light-client-proof-bitcoin/Cargo.toml @@ -14,9 +14,9 @@ anyhow = "1.0.68" bitcoin-da = { path = "../../../../../crates/bitcoin-da", default-features = false } citrea-light-client-prover = { path = "../../../../../crates/light-client-prover", default-features = false } citrea-primitives = { path = "../../../../../crates/primitives" } +citrea-risc0-adapter = { path = "../../../../../crates/risc0" } sov-modules-api = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-api", default-features = false } sov-modules-stf-blueprint = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-stf-blueprint" } -sov-risc0-adapter = { path = "../../../../../crates/sovereign-sdk/adapters/risc0" } sov-rollup-interface = { path = "../../../../../crates/sovereign-sdk/rollup-interface" } sov-state = { path = "../../../../../crates/sovereign-sdk/module-system/sov-state" } diff --git a/bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs b/bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs index 7673b0982..9bb7bdc9a 100644 --- a/bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs +++ b/bin/citrea/provers/risc0/light-client-proof-bitcoin/src/bin/light_client_proof_bitcoin.rs @@ -3,7 +3,7 @@ use bitcoin_da::spec::RollupParams; use bitcoin_da::verifier::BitcoinVerifier; use citrea_light_client_prover::circuit::run_circuit; use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; -use sov_risc0_adapter::guest::Risc0Guest; +use citrea_risc0_adapter::guest::Risc0Guest; use sov_rollup_interface::da::DaVerifier; use sov_rollup_interface::zk::ZkvmGuest; diff --git a/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock index 666a5720b..b76c1eedc 100644 --- a/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock +++ b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.lock @@ -731,6 +731,20 @@ dependencies = [ "sov-rollup-interface", ] +[[package]] +name = "citrea-risc0-adapter" +version = "0.5.0-rc.1" +dependencies = [ + "anyhow", + "bincode", + "borsh", + "hex", + "risc0-zkvm", + "serde", + "sov-rollup-interface", + "tracing", +] + [[package]] name = "const-hex" version = "1.13.1" @@ -1433,12 +1447,12 @@ dependencies = [ "anyhow", "citrea-light-client-prover", "citrea-primitives", + "citrea-risc0-adapter", "risc0-zkvm", "risc0-zkvm-platform", "sov-mock-da", "sov-modules-api", "sov-modules-stf-blueprint", - "sov-risc0-adapter", "sov-rollup-interface", "sov-state", ] @@ -2546,20 +2560,6 @@ dependencies = [ "sov-state", ] -[[package]] -name = "sov-risc0-adapter" -version = "0.5.0-rc.1" -dependencies = [ - "anyhow", - "bincode", - "borsh", - "bytemuck", - "risc0-zkvm", - "risc0-zkvm-platform", - "serde", - "sov-rollup-interface", -] - [[package]] name = "sov-rollup-interface" version = "0.5.0-rc.1" diff --git a/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml index 9778bba36..6dd94aaa4 100644 --- a/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml +++ b/bin/citrea/provers/risc0/light-client-proof-mock/Cargo.toml @@ -15,9 +15,9 @@ sov-mock-da = { path = "../../../../../crates/sovereign-sdk/adapters/mock-da", d citrea-light-client-prover = { path = "../../../../../crates/light-client-prover", default-features = false } citrea-primitives = { path = "../../../../../crates/primitives" } +citrea-risc0-adapter = { path = "../../../../../crates/risc0" } sov-modules-api = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-api", default-features = false } sov-modules-stf-blueprint = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-stf-blueprint" } -sov-risc0-adapter = { path = "../../../../../crates/sovereign-sdk/adapters/risc0" } sov-rollup-interface = { path = "../../../../../crates/sovereign-sdk/rollup-interface" } sov-state = { path = "../../../../../crates/sovereign-sdk/module-system/sov-state" } diff --git a/bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs b/bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs index 48f7b626b..ab50e1947 100644 --- a/bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs +++ b/bin/citrea/provers/risc0/light-client-proof-mock/src/bin/light_client_proof_mock.rs @@ -1,7 +1,7 @@ #![no_main] use citrea_light_client_prover::circuit::run_circuit; +use citrea_risc0_adapter::guest::Risc0Guest; use sov_mock_da::MockDaVerifier; -use sov_risc0_adapter::guest::Risc0Guest; use sov_rollup_interface::zk::ZkvmGuest; risc0_zkvm::guest::entry!(main); diff --git a/bin/citrea/src/rollup/bitcoin.rs b/bin/citrea/src/rollup/bitcoin.rs index 5ea493ca9..53e736cb7 100644 --- a/bin/citrea/src/rollup/bitcoin.rs +++ b/bin/citrea/src/rollup/bitcoin.rs @@ -8,8 +8,8 @@ use bitcoin_da::verifier::BitcoinVerifier; use citrea_common::rpc::register_healthcheck_rpc; use citrea_common::{BatchProverConfig, FullNodeConfig, LightClientProverConfig}; use citrea_primitives::{TO_BATCH_PROOF_PREFIX, TO_LIGHT_CLIENT_PREFIX}; -use citrea_risc0_bonsai_adapter::host::Risc0BonsaiHost; -use citrea_risc0_bonsai_adapter::Digest; +use citrea_risc0_adapter::host::Risc0BonsaiHost; +use citrea_risc0_adapter::Digest; // use citrea_sp1::host::SP1Host; use citrea_stf::genesis_config::StorageConfig; use citrea_stf::runtime::Runtime; @@ -174,12 +174,7 @@ impl RollupBlueprint for BitcoinRollup { da_service: &Arc, ledger_db: LedgerDB, ) -> Self::ProverService { - let vm = Risc0BonsaiHost::new( - 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(), - ); + let vm = Risc0BonsaiHost::new(citrea_risc0::BATCH_PROOF_BITCOIN_ELF, ledger_db.clone()); // let vm = SP1Host::new( // include_bytes!("../../provers/sp1/batch-prover-bitcoin/elf/zkvm-elf"), // ledger_db.clone(), @@ -223,8 +218,6 @@ impl RollupBlueprint for BitcoinRollup { ) -> Self::ProverService { let vm = Risc0BonsaiHost::new( 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(), ); let zk_stf = StfBlueprint::new(); diff --git a/bin/citrea/src/rollup/mock.rs b/bin/citrea/src/rollup/mock.rs index 5433ba711..ac19c5b76 100644 --- a/bin/citrea/src/rollup/mock.rs +++ b/bin/citrea/src/rollup/mock.rs @@ -5,8 +5,8 @@ use async_trait::async_trait; use citrea_common::rpc::register_healthcheck_rpc; use citrea_common::{BatchProverConfig, FullNodeConfig, LightClientProverConfig}; // use citrea_sp1::host::SP1Host; -use citrea_risc0_bonsai_adapter::host::Risc0BonsaiHost; -use citrea_risc0_bonsai_adapter::Digest; +use citrea_risc0_adapter::host::Risc0BonsaiHost; +use citrea_risc0_adapter::Digest; use citrea_stf::genesis_config::StorageConfig; use citrea_stf::runtime::Runtime; use citrea_stf::verifier::StateTransitionVerifier; @@ -120,12 +120,7 @@ impl RollupBlueprint for MockDemoRollup { da_service: &Arc, ledger_db: LedgerDB, ) -> Self::ProverService { - let vm = Risc0BonsaiHost::new( - 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(), - ); + let vm = Risc0BonsaiHost::new(citrea_risc0::BATCH_PROOF_MOCK_ELF, ledger_db.clone()); let zk_stf = StfBlueprint::new(); let zk_storage = ZkStorage::new(); @@ -152,12 +147,7 @@ impl RollupBlueprint for MockDemoRollup { da_service: &Arc, ledger_db: LedgerDB, ) -> Self::ProverService { - let vm = Risc0BonsaiHost::new( - 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(), - ); + let vm = Risc0BonsaiHost::new(citrea_risc0::LIGHT_CLIENT_PROOF_MOCK_ELF, ledger_db.clone()); let zk_stf = StfBlueprint::new(); let zk_storage = ZkStorage::new(); let da_verifier = Default::default(); diff --git a/crates/risc0-bonsai/src/host.rs b/crates/risc0-bonsai/src/host.rs deleted file mode 100644 index 0a5c5d18a..000000000 --- a/crates/risc0-bonsai/src/host.rs +++ /dev/null @@ -1,494 +0,0 @@ -//! This module implements the [`ZkvmHost`] trait for the RISC0 VM. -use std::thread; -use std::time::Duration; - -use anyhow::anyhow; -use backoff::exponential::ExponentialBackoffBuilder; -use backoff::{retry as retry_backoff, SystemClock}; -use bonsai_sdk::blocking::Client; -use borsh::{BorshDeserialize, BorshSerialize}; -use risc0_zkvm::sha::Digest; -use risc0_zkvm::{ - compute_image_id, AssumptionReceipt, ExecutorEnvBuilder, InnerReceipt, LocalProver, ProveInfo, - Prover, Receipt, -}; -use sov_db::ledger_db::{LedgerDB, ProvingServiceLedgerOps}; -use sov_risc0_adapter::guest::Risc0Guest; -use sov_risc0_adapter::host::add_benchmarking_callbacks; -use sov_rollup_interface::zk::{Proof, Zkvm, ZkvmHost}; -use tracing::{error, info, warn}; - -type StarkSessionId = String; -type SnarkSessionId = String; - -/// Bonsai sessions to be recovered in case of a crash. -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] -pub enum BonsaiSession { - /// Stark session id if the prover crashed during stark proof generation. - StarkSession(StarkSessionId), - /// Both Stark and Snark session id if the prover crashed during stark to snarkconversion. - SnarkSession(StarkSessionId, SnarkSessionId), -} - -/// Recovered bonsai session. -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] -pub struct RecoveredBonsaiSession { - /// Used for sending proofs in order - pub id: u8, - /// Recovered session - pub session: BonsaiSession, -} - -macro_rules! retry_backoff_bonsai { - ($bonsai_call:expr) => { - retry_backoff( - ExponentialBackoffBuilder::::new() - .with_initial_interval(Duration::from_secs(5)) - .with_max_elapsed_time(Some(Duration::from_secs(15 * 60))) - .build(), - || { - let response = $bonsai_call; - match response { - Ok(r) => Ok(r), - Err(e) => { - use ::bonsai_sdk::SdkErr::*; - match e { - InternalServerErr(s) => { - let err = format!("Got HHTP 500 from Bonsai: {}", s); - warn!(err); - Err(backoff::Error::transient(err)) - } - HttpErr(e) => { - let err = format!("Reconnecting to Bonsai: {}", e); - error!(err); - Err(backoff::Error::transient(err)) - } - HttpHeaderErr(e) => { - let err = format!("Reconnecting to Bonsai: {}", e); - error!(err); - Err(backoff::Error::transient(err)) - } - e => { - let err = format!("Got unrecoverable error from Bonsai: {}", e); - error!(err); - Err(backoff::Error::permanent(err)) - } - } - } - } - }, - ) - }; -} - -/// A [`Risc0BonsaiHost`] stores a binary to execute in the Risc0 VM and prove in the Risc0 Bonsai API. -#[derive(Clone)] -pub struct Risc0BonsaiHost<'a> { - elf: &'a [u8], - env: Vec, - assumptions: Vec, - image_id: Digest, - client: Option, - ledger_db: LedgerDB, -} - -impl<'a> Risc0BonsaiHost<'a> { - /// Create a new Risc0Host to prove the given binary. - pub fn new(elf: &'a [u8], api_url: String, api_key: String, ledger_db: LedgerDB) -> Self { - // Compute the image_id, then upload the ELF with the image_id as its key. - // handle error - let image_id = compute_image_id(elf).unwrap(); - - tracing::trace!("Calculated image id: {:?}", image_id.as_words()); - - // handle error - let client = if !api_url.is_empty() && !api_key.is_empty() { - tracing::debug!("Uploading image with id: {}", image_id); - let elf = elf.to_vec(); - thread::spawn(move || { - let client = Client::from_parts(api_url, api_key, risc0_zkvm::VERSION) - .expect("Failed to create Bonsai client; qed"); - - client - .upload_img(hex::encode(image_id).as_str(), elf) - .expect("Failed to upload image; qed"); - - tracing::debug!("Image uploaded"); - - Some(client) - }) - .join() - .unwrap() - } else { - None - }; - - Self { - elf, - env: Default::default(), - assumptions: vec![], - image_id, - client, - ledger_db, - } - } - - fn upload_to_bonsai(&self, client: &Client, buf: Vec) -> String { - let client = client.clone(); - let input_id = - thread::spawn(move || retry_backoff_bonsai!(client.upload_input(buf.clone()))) - .join() - .unwrap() - .expect("Failed to upload input; qed"); - tracing::info!("Uploaded input with id: {}", input_id); - input_id - } - - fn receipt_loop(&self, session: &str, client: &Client) -> Result, anyhow::Error> { - let session = bonsai_sdk::blocking::SessionId::new(session.to_owned()); - loop { - // handle error - let session = session.clone(); - let client_clone = client.clone(); - let res = thread::spawn(move || { - retry_backoff_bonsai!(session.status(&client_clone)) - .expect("Failed to fetch status; qed") - }) - .join() - .unwrap(); - - if res.status == "RUNNING" { - tracing::info!( - "Current status: {} - state: {} - continue polling...", - res.status, - res.state.unwrap_or_default() - ); - std::thread::sleep(Duration::from_secs(15)); - continue; - } - if res.status == "SUCCEEDED" { - // Download the receipt, containing the output - let receipt_url = res - .receipt_url - .expect("API error, missing receipt on completed session"); - - tracing::info!("Receipt URL: {}", receipt_url); - let client_clone = client.clone(); - let receipt_buf = thread::spawn(move || { - retry_backoff_bonsai!(client_clone.download(receipt_url.as_str())) - }) - .join() - .unwrap() - .expect("Failed to download receipt; qed"); - break Ok(receipt_buf); - } else { - return Err(anyhow!( - "Workflow exited: {} with error message: {}", - res.status, - res.error_msg.unwrap_or_default() - )); - } - } - } - - fn wait_for_receipt(&self, session: &str) -> Result, anyhow::Error> { - let session = bonsai_sdk::blocking::SessionId::new(session.to_string()); - let client = self.client.as_ref().unwrap(); - self.receipt_loop(&session.uuid, client) - } - - fn wait_for_stark_to_snark_conversion( - &self, - snark_session: Option<&str>, - stark_session: &str, - ) -> Result { - // If snark session exists use it else create one from stark - let snark_session = match snark_session { - Some(snark_session) => bonsai_sdk::blocking::SnarkId::new(snark_session.to_string()), - None => { - let client = self.client.clone().unwrap(); - let session = bonsai_sdk::blocking::SessionId::new(stark_session.to_string()); - thread::spawn(move || { - retry_backoff_bonsai!(client.create_snark(session.uuid.clone())) - .expect("Failed to create snark session; qed") - }) - .join() - .unwrap() - } - }; - - let recovered_serialized_snark_session = borsh::to_vec(&RecoveredBonsaiSession { - id: 0, - session: BonsaiSession::SnarkSession( - stark_session.to_string(), - snark_session.uuid.clone(), - ), - })?; - self.ledger_db - .add_pending_proving_session(recovered_serialized_snark_session.clone())?; - - let client = self.client.as_ref().unwrap(); - loop { - let snark_session = snark_session.clone(); - let client_clone = client.clone(); - let res = thread::spawn(move || { - retry_backoff_bonsai!(snark_session.status(&client_clone)) - .expect("Failed to fetch status; qed") - }) - .join() - .unwrap(); - match res.status.as_str() { - "RUNNING" => { - tracing::info!("Current status: {} - continue polling...", res.status,); - std::thread::sleep(Duration::from_secs(15)); - continue; - } - "SUCCEEDED" => { - let snark_receipt_url = match res.output { - Some(output) => output, - None => { - return Err(anyhow!( - "SNARK session succeeded but no output was provided" - )) - } - }; - - let client_clone = client.clone(); - let snark_receipt_buf = thread::spawn(move || { - retry_backoff_bonsai!(client_clone.download(snark_receipt_url.as_str())) - }) - .join() - .unwrap() - .expect("Failed to download receipt; qed"); - - let snark_receipt: Receipt = bincode::deserialize(&snark_receipt_buf.clone())?; - - tracing::info!("Snark proof!: {snark_receipt:?}"); - - return Ok(snark_receipt_buf); - } - _ => { - return Err(anyhow!( - "Workflow exited: {} with error message: {}", - res.status, - res.error_msg.unwrap_or_default() - )); - } - } - } - } -} - -impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { - type Guest = Risc0Guest; - - fn add_hint(&mut self, item: Vec) { - info!("Added hint to guest with size {}", item.len()); - - // write buf - self.env.extend_from_slice(&item); - } - - /// Guest simulation (execute mode) is run inside the Risc0 VM locally - fn simulate_with_hints(&mut self) -> Self::Guest { - 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) => { - // 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); - - receipt.verify(self.image_id)?; - - tracing::info!("Verified the fake receipt"); - - let serialized_receipt = bincode::serialize(&receipt)?; - - Ok(serialized_receipt) - } - // Local proving - (None, true) => { - 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; - - tracing::info!("Local proving completed"); - - receipt.verify(self.image_id)?; - - tracing::info!("Verified the receipt"); - - let serialized_receipt = bincode::serialize(&receipt)?; - - Ok(serialized_receipt) - } - // Bonsai proving - (Some(client), true) => { - // Upload input to Bonsai - let input_id = self.upload_to_bonsai(client, self.env.clone()); - - let image_id = hex::encode(self.image_id); - 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(), - vec![], - false - )) - .expect("Failed to fetch status; qed") - }) - .join() - .unwrap(); - let stark_session = RecoveredBonsaiSession { - id: 0, - session: BonsaiSession::StarkSession(session.uuid.clone()), - }; - let serialized_stark_session = borsh::to_vec(&stark_session) - .expect("Bonsai host should be able to serialize bonsai sessions"); - self.ledger_db - .add_pending_proving_session(serialized_stark_session.clone())?; - - tracing::info!("Session created: {}", session.uuid); - - let _receipt = self.wait_for_receipt(&session.uuid)?; - - tracing::info!("Creating the SNARK"); - - let client_clone = client.clone(); - let uuid = session.uuid.clone(); - let snark_session = thread::spawn(move || { - retry_backoff_bonsai!(client_clone.create_snark(uuid.clone())) - .expect("Failed to fetch status; qed") - }) - .join() - .unwrap(); - - // Remove the stark session as it is finished - self.ledger_db - .remove_pending_proving_session(serialized_stark_session)?; - - tracing::info!("SNARK session created: {}", snark_session.uuid); - - // Snark session is saved in the function - self.wait_for_stark_to_snark_conversion(Some(&snark_session.uuid), &session.uuid) - } - }?; - - // Cleanup env - self.env.clear(); - - // Cleanup assumptions - self.assumptions.clear(); - - Ok(proof) - } - - fn extract_output( - proof: &Proof, - ) -> Result, Self::Error> { - let receipt: Receipt = bincode::deserialize(proof)?; - let journal = receipt.journal; - - Ok(BorshDeserialize::try_from_slice(&journal.bytes)?) - } - - fn recover_proving_sessions(&self) -> Result, anyhow::Error> { - if self.client.is_none() { - tracing::debug!("Skipping bonsai session recovery"); - return Ok(vec![]); - } - - let sessions = self.ledger_db.get_pending_proving_sessions()?; - tracing::info!("Recovering {} bonsai sessions", sessions.len()); - let mut proofs = Vec::new(); - for session in sessions { - let bonsai_session: RecoveredBonsaiSession = BorshDeserialize::try_from_slice(&session) - .expect("Bonsai host should be able to recover bonsai sessions"); - - tracing::info!("Recovering bonsai session: {:?}", bonsai_session); - match bonsai_session.session { - BonsaiSession::StarkSession(stark_session) => { - let _receipt = self.wait_for_receipt(&stark_session)?; - let proof = self.wait_for_stark_to_snark_conversion(None, &stark_session)?; - proofs.push(proof); - } - BonsaiSession::SnarkSession(stark_session, snark_session) => { - let _receipt = self.wait_for_receipt(&stark_session)?; - let proof = self - .wait_for_stark_to_snark_conversion(Some(&snark_session), &stark_session)?; - proofs.push(proof) - } - } - } - Ok(proofs) - } -} - -impl<'host> Zkvm for Risc0BonsaiHost<'host> { - type CodeCommitment = Digest; - - type Error = anyhow::Error; - - fn verify( - serialized_proof: &[u8], - code_commitment: &Self::CodeCommitment, - ) -> Result, Self::Error> { - let receipt: Receipt = bincode::deserialize(serialized_proof)?; - - #[allow(clippy::clone_on_copy)] - receipt.verify(code_commitment.clone())?; - - Ok(receipt.journal.bytes) - } - - fn verify_and_extract_output( - serialized_proof: &[u8], - code_commitment: &Self::CodeCommitment, - ) -> Result, Self::Error> { - let receipt: Receipt = bincode::deserialize(serialized_proof)?; - - #[allow(clippy::clone_on_copy)] - receipt.verify(code_commitment.clone())?; - - Ok(BorshDeserialize::deserialize( - &mut receipt.journal.bytes.as_slice(), - )?) - } -} diff --git a/crates/risc0-bonsai/src/lib.rs b/crates/risc0-bonsai/src/lib.rs deleted file mode 100644 index 15cb3a716..000000000 --- a/crates/risc0-bonsai/src/lib.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![deny(missing_docs)] -//! # RISC0 Adapter -//! -//! This crate contains an adapter allowing the Risc0 to be used as a proof system for -//! Sovereign SDK rollups. -pub use risc0_zkvm::sha::Digest; -use serde::{Deserialize, Serialize}; -use sov_rollup_interface::zk::Matches; - -#[cfg(feature = "native")] -pub mod host; - -/// Uniquely identifies a Risc0 binary. Roughly equivalent to -/// the hash of the ELF file. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Risc0MethodId([u32; 8]); - -impl Risc0MethodId { - /// Create a new `Risc0MethodId` from a slice of u32s. - pub fn new(data: [u32; 8]) -> Self { - Self(data) - } - - /// Returns a reference to the `Risc0MethodId` as a slice of u32s. - pub fn as_words(&self) -> &[u32] { - &self.0 - } -} - -impl Matches for Risc0MethodId { - fn matches(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Matches for Risc0MethodId { - fn matches(&self, other: &Digest) -> bool { - self.0 == other.as_words() - } -} - -impl Matches<[u32; 8]> for Risc0MethodId { - fn matches(&self, other: &[u32; 8]) -> bool { - &self.0 == other - } -} diff --git a/crates/risc0-bonsai/Cargo.toml b/crates/risc0/Cargo.toml similarity index 76% rename from crates/risc0-bonsai/Cargo.toml rename to crates/risc0/Cargo.toml index ef7022cb4..18c46241c 100644 --- a/crates/risc0-bonsai/Cargo.toml +++ b/crates/risc0/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "citrea-risc0-bonsai-adapter" +name = "citrea-risc0-adapter" version = { workspace = true } authors = { workspace = true } edition = { workspace = true } @@ -7,13 +7,12 @@ homepage = { workspace = true } license = { workspace = true } readme = "README.md" repository = { workspace = true } -description = "An adapter allowing Citrea to connect with Bonsai" +description = "An adapter allowing Citrea to use risc0 proving system" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = { workspace = true } -backoff = { workspace = true, optional = true } bincode = { workspace = true } bonsai-sdk = { workspace = true, optional = true } borsh = { workspace = true } @@ -21,18 +20,15 @@ hex = { workspace = true } risc0-zkvm = { workspace = true, default-features = false, features = ["std"] } serde = { workspace = true } sov-db = { path = "../sovereign-sdk/full-node/db/sov-db", optional = true } -sov-risc0-adapter = { path = "../sovereign-sdk/adapters/risc0", optional = true } sov-rollup-interface = { path = "../sovereign-sdk/rollup-interface" } tracing = { workspace = true } [features] default = [] native = [ - "dep:backoff", "dep:bonsai-sdk", "dep:sov-db", "risc0-zkvm/prove", - "sov-risc0-adapter/native", "sov-rollup-interface/native", ] bench = ["native"] diff --git a/crates/risc0-bonsai/README.md b/crates/risc0/README.md similarity index 100% rename from crates/risc0-bonsai/README.md rename to crates/risc0/README.md diff --git a/crates/sovereign-sdk/adapters/risc0/src/guest/zkvm.rs b/crates/risc0/src/guest.rs similarity index 51% rename from crates/sovereign-sdk/adapters/risc0/src/guest/zkvm.rs rename to crates/risc0/src/guest.rs index 4879c9b72..46f832b26 100644 --- a/crates/sovereign-sdk/adapters/risc0/src/guest/zkvm.rs +++ b/crates/risc0/src/guest.rs @@ -2,7 +2,9 @@ use borsh::{BorshDeserialize, BorshSerialize}; use risc0_zkvm::guest::env; use risc0_zkvm::guest::env::Write; -use sov_rollup_interface::zk::ZkvmGuest; +use sov_rollup_interface::zk::{Zkvm, ZkvmGuest}; + +use crate::Risc0MethodId; /// A guest for the RISC0 VM. Implements the `ZkvmGuest` trait /// in terms of Risc0's env::read and env::commit functions. @@ -31,3 +33,29 @@ impl ZkvmGuest for Risc0Guest { journal.write_slice(&buf); } } + +impl Zkvm for Risc0Guest { + type CodeCommitment = Risc0MethodId; + + type Error = anyhow::Error; + + fn verify( + journal: &[u8], + code_commitment: &Self::CodeCommitment, + ) -> Result, Self::Error> { + env::verify(code_commitment.0, journal) + .expect("Guest side verification error should be Infallible"); + Ok(journal.to_vec()) + } + + fn verify_and_extract_output( + journal: &[u8], + code_commitment: &Self::CodeCommitment, + ) -> Result, Self::Error> { + 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/risc0/src/host.rs b/crates/risc0/src/host.rs new file mode 100644 index 000000000..eafc5c08f --- /dev/null +++ b/crates/risc0/src/host.rs @@ -0,0 +1,237 @@ +//! This module implements the [`ZkvmHost`] trait for the RISC0 VM. + +use borsh::{BorshDeserialize, BorshSerialize}; +use risc0_zkvm::sha::Digest; +use risc0_zkvm::{ + compute_image_id, default_prover, AssumptionReceipt, ExecutorEnvBuilder, ProveInfo, ProverOpts, + Receipt, +}; +use sov_db::ledger_db::LedgerDB; +use sov_rollup_interface::zk::{Proof, Zkvm, ZkvmHost}; +use tracing::{debug, info}; + +use crate::guest::Risc0Guest; + +type StarkSessionId = String; +type SnarkSessionId = String; + +/// Bonsai sessions to be recovered in case of a crash. +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub enum BonsaiSession { + /// Stark session id if the prover crashed during stark proof generation. + StarkSession(StarkSessionId), + /// Both Stark and Snark session id if the prover crashed during stark to snarkconversion. + SnarkSession(StarkSessionId, SnarkSessionId), +} + +/// Recovered bonsai session. +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub struct RecoveredBonsaiSession { + /// Used for sending proofs in order + pub id: u8, + /// Recovered session + pub session: BonsaiSession, +} + +/// A [`Risc0BonsaiHost`] stores a binary to execute in the Risc0 VM and prove in the Risc0 Bonsai API. +#[derive(Clone)] +pub struct Risc0BonsaiHost<'a> { + elf: &'a [u8], + env: Vec, + assumptions: Vec, + image_id: Digest, + _ledger_db: LedgerDB, +} + +impl<'a> Risc0BonsaiHost<'a> { + /// Create a new Risc0Host to prove the given binary. + pub fn new(elf: &'a [u8], ledger_db: LedgerDB) -> Self { + // Compute the image_id, then upload the ELF with the image_id as its key. + // handle error + let image_id = compute_image_id(elf).unwrap(); + + tracing::trace!("Calculated image id: {:?}", image_id.as_words()); + + match std::env::var("RISC0_PROVER") { + Ok(prover) => match prover.as_str() { + "bonsai" => { + if std::env::var("BONSAI_API_URL").is_err() + || std::env::var("BONSAI_API_KEY").is_err() + { + panic!("Bonsai API URL and API key must be set when RISC0_PROVER is set to bonsai"); + } + } + "local" => {} + "ipc" => { + if std::env::var("RISC0_SERVER_PATH").is_err() { + panic!("RISC0_SERVER_PATH must be set when RISC0_PROVER is set to ipc"); + } + } + _ => { + panic!("Invalid prover specified: {}", prover); + } + }, + Err(_) => { + debug!("No prover specified."); + + if std::env::var("BONSAI_API_URL").is_ok() + && std::env::var("BONSAI_API_KEY").is_ok() + { + panic!( + "Bonsai API URL and API key are set, but RISC0_PROVER is not set to bonsai" + ); + } + } + } + + Self { + elf, + env: Default::default(), + assumptions: vec![], + image_id, + _ledger_db: ledger_db, + } + } +} + +impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { + type Guest = Risc0Guest; + + fn add_hint(&mut self, item: Vec) { + info!("Added hint to guest with size {}", item.len()); + + // write buf + self.env.extend_from_slice(&item); + } + + /// Guest simulation (execute mode) is run inside the Risc0 VM locally + fn simulate_with_hints(&mut self) -> Self::Guest { + todo!("we don't use it yet") + } + + 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 { + if !with_proof { + if std::env::var("RISC0_PROVER") == Ok("bonsai".to_string()) { + panic!("Bonsai prover requires with_proof to be true"); + } + + std::env::set_var("RISC0_DEV_MODE", "1"); + } + + let mut env = 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(); + + // The `RISC0_PROVER` environment variable, if specified, will select the + // following [Prover] implementation: + // * `bonsai`: [BonsaiProver] to prove on Bonsai. + // * `local`: LocalProver to prove locally in-process. Note: this + // requires the `prove` feature flag. + // * `ipc`: [ExternalProver] to prove using an `r0vm` sub-process. Note: `r0vm` + // must be installed. To specify the path to `r0vm`, use `RISC0_SERVER_PATH`. + let prover = default_prover(); + + tracing::info!("Starting risc0 proving"); + let ProveInfo { receipt, stats } = + prover.prove_with_opts(env, self.elf, &ProverOpts::groth16())?; + + tracing::info!("Execution Stats: {:?}", stats); + + receipt.verify(self.image_id)?; + + tracing::info!("Verified the receipt"); + + let serialized_receipt = bincode::serialize(&receipt)?; + + // Cleanup env + self.env.clear(); + + // Cleanup assumptions + self.assumptions.clear(); + + Ok(serialized_receipt) + } + + fn extract_output( + proof: &Proof, + ) -> Result, Self::Error> { + let receipt: Receipt = bincode::deserialize(proof)?; + let journal = receipt.journal; + + Ok(BorshDeserialize::try_from_slice(&journal.bytes)?) + } + + fn recover_proving_sessions(&self) -> Result, anyhow::Error> { + Ok(Vec::new()) + + // TODO: fix this https://github.com/chainwayxyz/citrea/issues/1410 + // + // let sessions = self.ledger_db.get_pending_proving_sessions()?; + // tracing::info!("Recovering {} bonsai sessions", sessions.len()); + // let mut proofs = Vec::new(); + // for session in sessions { + // let bonsai_session: RecoveredBonsaiSession = BorshDeserialize::try_from_slice(&session) + // .expect("Bonsai host should be able to recover bonsai sessions"); + + // tracing::info!("Recovering bonsai session: {:?}", bonsai_session); + // match bonsai_session.session { + // BonsaiSession::StarkSession(stark_session) => { + // let _receipt = self.wait_for_receipt(&stark_session)?; + // let proof = self.wait_for_stark_to_snark_conversion(None, &stark_session)?; + // proofs.push(proof); + // } + // BonsaiSession::SnarkSession(stark_session, snark_session) => { + // let _receipt = self.wait_for_receipt(&stark_session)?; + // let proof = self + // .wait_for_stark_to_snark_conversion(Some(&snark_session), &stark_session)?; + // proofs.push(proof) + // } + // } + // } + // Ok(proofs) + } +} + +impl<'host> Zkvm for Risc0BonsaiHost<'host> { + type CodeCommitment = Digest; + + type Error = anyhow::Error; + + fn verify( + serialized_proof: &[u8], + code_commitment: &Self::CodeCommitment, + ) -> Result, Self::Error> { + let receipt: Receipt = bincode::deserialize(serialized_proof)?; + + #[allow(clippy::clone_on_copy)] + receipt.verify(code_commitment.clone())?; + + Ok(receipt.journal.bytes) + } + + fn verify_and_extract_output( + serialized_proof: &[u8], + code_commitment: &Self::CodeCommitment, + ) -> Result, Self::Error> { + let receipt: Receipt = bincode::deserialize(serialized_proof)?; + + #[allow(clippy::clone_on_copy)] + receipt.verify(code_commitment.clone())?; + + Ok(BorshDeserialize::deserialize( + &mut receipt.journal.bytes.as_slice(), + )?) + } +} diff --git a/crates/sovereign-sdk/adapters/risc0/src/lib.rs b/crates/risc0/src/lib.rs similarity index 94% rename from crates/sovereign-sdk/adapters/risc0/src/lib.rs rename to crates/risc0/src/lib.rs index 48d32cc38..c532e107b 100644 --- a/crates/sovereign-sdk/adapters/risc0/src/lib.rs +++ b/crates/risc0/src/lib.rs @@ -3,7 +3,7 @@ //! //! This crate contains an adapter allowing the Risc0 to be used as a proof system for //! Sovereign SDK rollups. -use risc0_zkvm::sha::Digest; +pub use risc0_zkvm::sha::Digest; use serde::{Deserialize, Serialize}; use sov_rollup_interface::zk::Matches; @@ -11,9 +11,6 @@ pub mod guest; #[cfg(feature = "native")] pub mod host; -#[cfg(feature = "bench")] -pub mod metrics; - /// Uniquely identifies a Risc0 binary. Roughly equivalent to /// the hash of the ELF file. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/sovereign-sdk/adapters/risc0/Cargo.toml b/crates/sovereign-sdk/adapters/risc0/Cargo.toml deleted file mode 100644 index b039eeec5..000000000 --- a/crates/sovereign-sdk/adapters/risc0/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "sov-risc0-adapter" -authors = { workspace = true } -description = "An adapter allowing Risc0 to be used with the Sovereign SDK" -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -version = { workspace = true } -readme = "README.md" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = { workspace = true } -bincode = { workspace = true } -borsh = { workspace = true } -risc0-zkvm = { workspace = true, default-features = false, features = ["std"] } -risc0-zkvm-platform = { workspace = true } -risc0-zkp = { workspace = true, optional = true } -risc0-circuit-rv32im = { workspace = true, optional = true } -serde = { workspace = true } -bytemuck = "1.13.1" -once_cell = { version = "1.19.0", optional = true } -parking_lot = { version = "0.12.1", optional = true } -sov-zk-cycle-utils = { path = "../../utils/zk-cycle-utils", optional = true } -sov-rollup-interface = { path = "../../rollup-interface" } - -[features] -default = [] -native = ["risc0-zkvm/prove", "dep:risc0-zkp", "dep:risc0-circuit-rv32im"] -bench = ["once_cell", "parking_lot", "native", "sov-zk-cycle-utils/native"] diff --git a/crates/sovereign-sdk/adapters/risc0/README.md b/crates/sovereign-sdk/adapters/risc0/README.md deleted file mode 100644 index f0d2fe74d..000000000 --- a/crates/sovereign-sdk/adapters/risc0/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Risc0 Adapter - -This package adapts Risc0 version 0.19 to work as a zkVM for the Sovereign SDK. - -## Limitations - -While in-VM recursion is included in the Risc0 0.19 release, this adapter doesn't currently implement it. Individual "slots" may be proven, but those proofs cannot be recursively combined to facilitate bridging or ultra-fast sync ("user recursion" is not supported). - -## Warning - -Risc0 is currently under active development and has not been audited. This adapter has also not been audited. Do not -deploy in production diff --git a/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs b/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs deleted file mode 100644 index f52e88d6d..000000000 --- a/crates/sovereign-sdk/adapters/risc0/src/guest/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! This module implements the `ZkvmGuest` trait for the RISC0 VM. -//! However the implementation is different -//! for host(native) and guest(zkvm) part. -//! 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; - -// Here goes the host/guest implementation: - -#[cfg(not(target_os = "zkvm"))] -mod native; -#[cfg(target_os = "zkvm")] -mod zkvm; - -#[cfg(not(target_os = "zkvm"))] -pub use native::Risc0Guest; -#[cfg(target_os = "zkvm")] -pub use zkvm::Risc0Guest; - -impl Zkvm for Risc0Guest { - type CodeCommitment = Risc0MethodId; - - type Error = anyhow::Error; - - fn verify( - journal: &[u8], - code_commitment: &Self::CodeCommitment, - ) -> Result, Self::Error> { - env::verify(code_commitment.0, journal) - .expect("Guest side verification error should be Infallible"); - Ok(journal.to_vec()) - } - - fn verify_and_extract_output( - journal: &[u8], - code_commitment: &Self::CodeCommitment, - ) -> Result, Self::Error> { - 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/guest/native.rs b/crates/sovereign-sdk/adapters/risc0/src/guest/native.rs deleted file mode 100644 index 68457b497..000000000 --- a/crates/sovereign-sdk/adapters/risc0/src/guest/native.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! This module implements the `ZkvmGuest` trait for the RISC0 VM. - -use std::io::Cursor; - -use borsh::{BorshDeserialize, BorshSerialize}; -use sov_rollup_interface::zk::ZkvmGuest; - -#[derive(Default)] -struct Hints { - cursor: Cursor>, -} - -impl Hints { - pub fn with_hints(hints: Vec) -> Self { - Hints { - cursor: Cursor::new(hints), - } - } -} - -/// A guest for the RISC0 VM. Implements the `ZkvmGuest` trait -/// using interior mutability to test the functionality. -#[derive(Default)] -pub struct Risc0Guest { - hints: std::sync::Mutex, - // commits: std::sync::Mutex>, -} - -impl Risc0Guest { - /// Constructs a new Risc0 Guest - pub fn new() -> Self { - Self::default() - } - - /// Constructs a new Risc0 Guest with the provided hints. - pub fn with_hints(hints: Vec) -> Self { - Self { - hints: std::sync::Mutex::new(Hints::with_hints(hints)), - // commits: Default::default(), - } - } -} - -impl ZkvmGuest for Risc0Guest { - fn read_from_host(&self) -> T { - let mut hints = self.hints.lock().unwrap(); - let cursor = &mut hints.cursor; - // deserialize - BorshDeserialize::deserialize_reader(&mut *cursor).unwrap() - } - - fn commit(&self, _item: &T) { - unimplemented!("commitment never used in a test code") - // self.commits.lock().unwrap().extend_from_slice( - // &risc0_zkvm::serde::to_vec(item).expect("Serialization to vec is infallible"), - // ); - } -} diff --git a/crates/sovereign-sdk/adapters/risc0/src/host.rs b/crates/sovereign-sdk/adapters/risc0/src/host.rs deleted file mode 100644 index 61f146592..000000000 --- a/crates/sovereign-sdk/adapters/risc0/src/host.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! RISC0 Host helpers -use risc0_zkvm::ExecutorEnvBuilder; - -#[cfg(not(feature = "bench"))] -/// Add benchmarking callbacks to the executor environment. -pub fn add_benchmarking_callbacks(env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBuilder<'_> { - env -} - -#[cfg(feature = "bench")] -/// Add benchmarking callbacks to the executor environment. -pub fn add_benchmarking_callbacks(mut env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBuilder<'_> { - use sov_zk_cycle_utils::{cycle_count_callback, get_syscall_name, get_syscall_name_cycles}; - - use crate::metrics::metrics_callback; - - let metrics_syscall_name = get_syscall_name(); - env.io_callback(metrics_syscall_name, metrics_callback); - - let cycles_syscall_name = get_syscall_name_cycles(); - env.io_callback(cycles_syscall_name, cycle_count_callback); - - env -} diff --git a/crates/sovereign-sdk/adapters/risc0/src/metrics.rs b/crates/sovereign-sdk/adapters/risc0/src/metrics.rs deleted file mode 100644 index d217f2b35..000000000 --- a/crates/sovereign-sdk/adapters/risc0/src/metrics.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Defines utilities for collecting runtime metrics from inside a Risc0 VM -use std::collections::HashMap; - -use anyhow::Context; -use once_cell::sync::Lazy; -use parking_lot::Mutex; -use risc0_zkvm::Bytes; - -/// A global hashmap mapping metric names to their values. -pub static GLOBAL_HASHMAP: Lazy>> = - Lazy::new(|| Mutex::new(HashMap::new())); - -/// Increments the requested metric by the given value, creating a -/// new entry in the global map if necessary. -fn add_value(metric: String, value: u64) { - let mut hashmap = GLOBAL_HASHMAP.lock(); - hashmap - .entry(metric) - .and_modify(|(sum, count)| { - *sum += value; - *count += 1; - }) - .or_insert((value, 1)); -} - -/// Deserialize a `Bytes` into a null-separated `(String, u64)` tuple. This function -/// expects its arguments to match the format of arguments to Risc0's io callbacks. -fn deserialize_custom(serialized: Bytes) -> Result<(String, u64), anyhow::Error> { - let null_pos = serialized - .iter() - .position(|&b| b == 0) - .context("Could not find separator in provided bytes")?; - let (string_bytes, size_bytes_with_null) = serialized.split_at(null_pos); - let size_bytes = &size_bytes_with_null[1..]; // Skip the null terminator - let string = String::from_utf8(string_bytes.to_vec())?; - let size = u64::from_ne_bytes(size_bytes.try_into()?); // Convert bytes back into usize - Ok((string, size)) -} - -/// A custom callback for extracting metrics from the Risc0 zkvm. -/// -/// When the "bench" feature is enabled, this callback is registered as a syscall -/// in the Risc0 VM and invoked whenever a function annotated with the [`sov-zk-cycle-utils::cycle_tracker`] -/// macro is invoked. -pub fn metrics_callback(input: Bytes) -> Result { - let met_tuple = deserialize_custom(input)?; - add_value(met_tuple.0, met_tuple.1); - Ok(Bytes::new()) -}