From fc6dfe04586fd7a0a6e64aa220407cdce7285ee4 Mon Sep 17 00:00:00 2001 From: Esad Yusuf Atik Date: Sat, 18 May 2024 00:13:24 +0300 Subject: [PATCH] introduce bitcoin prover (#584) * introduce bitcoin prover * - check that l2 block da heder points to previous da header - implement send_transaction in bitcoin da * lint * review * fix udeps check * post merge fix --- Cargo.lock | 2 - bin/citrea/Cargo.toml | 5 +- bin/citrea/provers/risc0/Cargo.toml | 2 +- bin/citrea/provers/risc0/build.rs | 2 +- .../provers/risc0/guest-bitcoin/Cargo.lock | 10 --- .../provers/risc0/guest-bitcoin/Cargo.toml | 51 ++++++++++++++++ .../risc0/guest-bitcoin/src/bin/bitcoin_da.rs | 61 +++++++++++++++++++ .../provers/risc0/guest-mock/Cargo.lock | 10 --- bin/citrea/src/bitcoin_rollup.rs | 11 ++-- crates/bitcoin-da/src/helpers/builders.rs | 12 ++-- crates/bitcoin-da/src/service.rs | 11 +++- crates/bitcoin-da/src/spec/mod.rs | 1 + .../adapters/risc0-bonsai/Cargo.toml | 12 ++-- .../adapters/risc0-bonsai/src/host.rs | 30 ++------- .../sovereign-sdk/adapters/risc0/Cargo.toml | 4 +- .../sovereign-sdk/adapters/risc0/src/host.rs | 6 +- .../sov-modules-stf-blueprint/src/lib.rs | 6 +- 17 files changed, 162 insertions(+), 74 deletions(-) create mode 100644 bin/citrea/provers/risc0/guest-bitcoin/Cargo.toml create mode 100644 bin/citrea/provers/risc0/guest-bitcoin/src/bin/bitcoin_da.rs diff --git a/Cargo.lock b/Cargo.lock index 26f800cee..0a2af1cff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1617,7 +1617,6 @@ dependencies = [ "sov-modules-rollup-blueprint", "sov-modules-stf-blueprint", "sov-prover-storage-manager", - "sov-risc0-adapter", "sov-rollup-interface", "sov-state", "sov-stf-runner", @@ -1692,7 +1691,6 @@ dependencies = [ "serde", "sov-risc0-adapter", "sov-rollup-interface", - "sov-zk-cycle-utils", "tracing", ] diff --git a/bin/citrea/Cargo.toml b/bin/citrea/Cargo.toml index 15bbe388d..cd774d321 100644 --- a/bin/citrea/Cargo.toml +++ b/bin/citrea/Cargo.toml @@ -57,9 +57,6 @@ sov-db = { path = "../../crates/sovereign-sdk/full-node/db/sov-db" } ethereum-rpc = { path = "../../crates/ethereum-rpc" } sequencer-client = { path = "../../crates/sequencer-client" } citrea-sequencer = { path = "../../crates/sequencer" } -sov-risc0-adapter = { path = "../../crates/sovereign-sdk/adapters/risc0", features = [ - "native", -] } citrea-risc0-bonsai-adapter = { path = "../../crates/sovereign-sdk/adapters/risc0-bonsai", features = [ "native", ] } @@ -112,7 +109,7 @@ regex = "1.10" default = [ ] # Deviate from convention by making the "native" feature active by default. This aligns with how this package is meant to be used (as a binary first, library second). -bench = ["hex", "sov-risc0-adapter/bench", "risc0/bench"] +bench = ["hex"] #"sov-risc0-adapter/bench", "risc0/bench"] [[bin]] name = "citrea" diff --git a/bin/citrea/provers/risc0/Cargo.toml b/bin/citrea/provers/risc0/Cargo.toml index 297d99d01..9b9ceec3b 100644 --- a/bin/citrea/provers/risc0/Cargo.toml +++ b/bin/citrea/provers/risc0/Cargo.toml @@ -10,7 +10,7 @@ publish = false risc0-build = { workspace = true } [package.metadata.risc0] -methods = ["guest-mock"] +methods = ["guest-mock", "guest-bitcoin"] [features] bench = [] diff --git a/bin/citrea/provers/risc0/build.rs b/bin/citrea/provers/risc0/build.rs index 4915f8016..a06855fc5 100644 --- a/bin/citrea/provers/risc0/build.rs +++ b/bin/citrea/provers/risc0/build.rs @@ -11,7 +11,7 @@ fn main() { let methods_path = out_dir.join("methods.rs"); let elf = r#" - pub const BITCOIN_ELF: &[u8] = &[]; + pub const BITCOIN_DA_ELF: &[u8] = &[]; pub const MOCK_DA_ELF: &[u8] = &[]; "#; diff --git a/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock b/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock index 4991ede15..9b248ba69 100644 --- a/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock +++ b/bin/citrea/provers/risc0/guest-bitcoin/Cargo.lock @@ -3011,7 +3011,6 @@ dependencies = [ "risc0-zkvm-platform", "serde", "sov-rollup-interface", - "sov-zk-cycle-utils", ] [[package]] @@ -3075,15 +3074,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "sov-zk-cycle-utils" -version = "0.3.0" -dependencies = [ - "bytes", - "risc0-zkvm", - "risc0-zkvm-platform", -] - [[package]] name = "spin" version = "0.5.2" diff --git a/bin/citrea/provers/risc0/guest-bitcoin/Cargo.toml b/bin/citrea/provers/risc0/guest-bitcoin/Cargo.toml new file mode 100644 index 000000000..ada8a8f15 --- /dev/null +++ b/bin/citrea/provers/risc0/guest-bitcoin/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "citrea-bitcoin-prover" +version = "0.3.0" +edition = "2021" +resolver = "2" + +[workspace] + +[dependencies] +anyhow = "1.0.68" +risc0-zkvm = { version = "0.21", default-features = false, features = ["std"] } +risc0-zkvm-platform = "0.21" +bitcoin-da = { path = "../../../../../crates/bitcoin-da", default-features = false } +rollup-constants = { path = "../../../../rollup-constants" } +citrea-stf = { path = "../../../../../crates/citrea-stf" } +sov-risc0-adapter = { path = "../../../../../crates/sovereign-sdk/adapters/risc0" } +sov-modules-api = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-api", default-features = false } +sov-state = { path = "../../../../../crates/sovereign-sdk/module-system/sov-state" } +sov-modules-stf-blueprint = { path = "../../../../../crates/sovereign-sdk/module-system/sov-modules-stf-blueprint" } +sov-rollup-interface = { path = "../../../../../crates/sovereign-sdk/rollup-interface" } +# forcing cargo for this version or else chooses 3.1.1 and there is some dependency conflicts +revm-primitives = { version = "=3.1.0", default-features = false } +# forcing cargo for this version or else chooses 0.3.1 and there is some dependency conflicts +alloy-trie = { version = "=0.3.0", default-features = false } + +[patch.crates-io] +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" } +ed25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.0-risczero.1" } +crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.2-risc0" } +secp256k1_v027 = { package = "secp256k1", version = "0.27", git = "https://github.com/Sovereign-Labs/rust-secp256k1.git", branch = "risc0-compatible-0-27-0" } +secp256k1_v028 = { package = "secp256k1", version = "0.28", git = "https://github.com/Sovereign-Labs/rust-secp256k1.git", branch = "risc0-compatible-0-28-2" } + +[profile.dev] +opt-level = 3 + +[profile.dev.build-override] +opt-level = 3 + +[profile.release] +debug = 1 +lto = true + +[profile.release.build-override] +opt-level = 3 + +[features] +bench = [ + "sov-modules-api/bench", + "sov-state/bench", + "sov-modules-stf-blueprint/bench", +] diff --git a/bin/citrea/provers/risc0/guest-bitcoin/src/bin/bitcoin_da.rs b/bin/citrea/provers/risc0/guest-bitcoin/src/bin/bitcoin_da.rs new file mode 100644 index 000000000..9f31bb29a --- /dev/null +++ b/bin/citrea/provers/risc0/guest-bitcoin/src/bin/bitcoin_da.rs @@ -0,0 +1,61 @@ +#![no_main] +use bitcoin_da::spec::RollupParams; +use bitcoin_da::verifier::BitcoinVerifier; + +use citrea_stf::runtime::Runtime; +use citrea_stf::StfVerifier; +#[cfg(feature = "bench")] +use risc0_zkvm::guest::env; +use rollup_constants::{DA_TX_ID_LEADING_ZEROS, ROLLUP_NAME}; +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; + +#[cfg(feature = "bench")] +fn report_bench_metrics(start_cycles: usize, end_cycles: usize) { + let cycles_per_block = (end_cycles - start_cycles) as u64; + let tuple = ("Cycles per block".to_string(), cycles_per_block); + let mut serialized = Vec::new(); + serialized.extend(tuple.0.as_bytes()); + serialized.push(0); + let size_bytes = tuple.1.to_ne_bytes(); + serialized.extend(&size_bytes); + + // calculate the syscall name. + let cycle_string = String::from("cycle_metrics\0"); + let metrics_syscall_name = + risc0_zkvm_platform::syscall::SyscallName::from_bytes_with_nul(cycle_string.as_ptr()); + + risc0_zkvm::guest::env::send_recv_slice::(metrics_syscall_name, &serialized); +} + +risc0_zkvm::guest::entry!(main); + +pub fn main() { + let guest = Risc0Guest::new(); + let storage = ZkStorage::new(); + #[cfg(feature = "bench")] + let start_cycles = env::cycle_count(); + + let stf: StfBlueprint> = StfBlueprint::new(); + + let stf_verifier = StfVerifier::new( + stf, + BitcoinVerifier::new(RollupParams { + rollup_name: ROLLUP_NAME.to_string(), + reveal_tx_id_prefix: DA_TX_ID_LEADING_ZEROS.to_vec(), + }), + ); + + stf_verifier + .run_sequencer_commitments_in_da_slot(guest, storage) + .expect("Prover must be honest"); + + #[cfg(feature = "bench")] + { + let end_cycles = env::cycle_count(); + report_bench_metrics(start_cycles, end_cycles); + } +} diff --git a/bin/citrea/provers/risc0/guest-mock/Cargo.lock b/bin/citrea/provers/risc0/guest-mock/Cargo.lock index 9cfb207ee..0a0a88c0f 100644 --- a/bin/citrea/provers/risc0/guest-mock/Cargo.lock +++ b/bin/citrea/provers/risc0/guest-mock/Cargo.lock @@ -2799,7 +2799,6 @@ dependencies = [ "risc0-zkvm-platform", "serde", "sov-rollup-interface", - "sov-zk-cycle-utils", ] [[package]] @@ -2863,15 +2862,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "sov-zk-cycle-utils" -version = "0.3.0" -dependencies = [ - "bytes", - "risc0-zkvm", - "risc0-zkvm-platform", -] - [[package]] name = "spin" version = "0.5.2" diff --git a/bin/citrea/src/bitcoin_rollup.rs b/bin/citrea/src/bitcoin_rollup.rs index b406ad4bc..cace68dc0 100644 --- a/bin/citrea/src/bitcoin_rollup.rs +++ b/bin/citrea/src/bitcoin_rollup.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use bitcoin_da::service::{BitcoinService, DaServiceConfig}; use bitcoin_da::spec::{BitcoinSpec, RollupParams}; use bitcoin_da::verifier::BitcoinVerifier; +use citrea_risc0_bonsai_adapter::host::Risc0BonsaiHost; use citrea_stf::genesis_config::StorageConfig; use citrea_stf::runtime::Runtime; use rollup_constants::{DA_TX_ID_LEADING_ZEROS, ROLLUP_NAME}; @@ -11,7 +12,6 @@ use sov_modules_api::{Address, Spec}; use sov_modules_rollup_blueprint::RollupBlueprint; use sov_modules_stf_blueprint::StfBlueprint; use sov_prover_storage_manager::ProverStorageManager; -use sov_risc0_adapter::host::Risc0Host; use sov_rollup_interface::da::DaVerifier; use sov_rollup_interface::zk::ZkvmHost; use sov_state::{DefaultStorageSpec, Storage, ZkStorage}; @@ -25,7 +25,7 @@ impl RollupBlueprint for BitcoinRollup { type DaService = BitcoinService; type DaSpec = BitcoinSpec; type DaConfig = DaServiceConfig; - type Vm = Risc0Host<'static>; + type Vm = Risc0BonsaiHost<'static>; type ZkContext = ZkDefaultContext; type NativeContext = DefaultContext; @@ -104,8 +104,11 @@ impl RollupBlueprint for BitcoinRollup { _rollup_config: &RollupConfig, _da_service: &Self::DaService, ) -> Self::ProverService { - // TODO: will be BITCOIN_ELF - let vm = Risc0Host::new(risc0::MOCK_DA_ELF); + let vm = Risc0BonsaiHost::new( + risc0::BITCOIN_DA_ELF, + std::env::var("BONSAI_API_URL").unwrap_or("".to_string()), + std::env::var("BONSAI_API_KEY").unwrap_or("".to_string()), + ); let zk_stf = StfBlueprint::new(); let zk_storage = ZkStorage::new(); diff --git a/crates/bitcoin-da/src/helpers/builders.rs b/crates/bitcoin-da/src/helpers/builders.rs index d7b641163..2fc09a7c6 100644 --- a/crates/bitcoin-da/src/helpers/builders.rs +++ b/crates/bitcoin-da/src/helpers/builders.rs @@ -191,10 +191,10 @@ fn build_commit_transaction( let mut last_size = size; let tx = loop { - if iteration % 100 == 0 { + if iteration % 10 == 0 { trace!(iteration, "Trying to find commitment size"); - if iteration > 5000 { - warn!("Too many iterations"); + if iteration > 100 { + warn!("Too many iterations choosing UTXOs"); } } let fee = ((last_size as f64) * fee_rate).ceil() as u64; @@ -379,10 +379,10 @@ pub fn create_inscription_transactions( // Start loop to find a 'nonce' i.e. random number that makes the reveal tx hash starting with zeros given length let mut nonce: i64 = 0; loop { - if nonce % 100 == 0 { + if nonce % 10000 == 0 { trace!(nonce, "Trying to find commit & reveal nonce"); - if nonce > 1000 { - warn!("Too many iterations"); + if nonce > 65536 { + warn!("Too many iterations finding nonce"); } } let utxos = utxos.clone(); diff --git a/crates/bitcoin-da/src/service.rs b/crates/bitcoin-da/src/service.rs index 15a355ae2..6ac6ead13 100644 --- a/crates/bitcoin-da/src/service.rs +++ b/crates/bitcoin-da/src/service.rs @@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize}; use sov_rollup_interface::da::DaSpec; use sov_rollup_interface::services::da::{BlobWithNotifier, DaService}; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; +use tokio::sync::oneshot::channel as oneshot_channel; use tracing::{error, info, instrument, trace}; use crate::helpers::builders::{ @@ -499,9 +500,15 @@ impl DaService for BitcoinService { #[instrument(level = "trace", skip_all)] async fn send_transaction( &self, - _blob: &[u8], + blob: &[u8], ) -> Result<::TransactionId, Self::Error> { - unimplemented!("Use send_tx_no_wait instead") + let queue = self.get_send_transaction_queue(); + let (tx, rx) = oneshot_channel(); + queue.send(BlobWithNotifier { + blob: blob.to_vec(), + notify: tx, + })?; + rx.await? } fn get_send_transaction_queue(&self) -> UnboundedSender> { diff --git a/crates/bitcoin-da/src/spec/mod.rs b/crates/bitcoin-da/src/spec/mod.rs index 9bf097b52..f038e20ee 100644 --- a/crates/bitcoin-da/src/spec/mod.rs +++ b/crates/bitcoin-da/src/spec/mod.rs @@ -13,6 +13,7 @@ pub mod blob; pub mod block; mod block_hash; pub mod header; +#[cfg(feature = "native")] pub mod header_stream; pub mod proof; pub mod transaction; diff --git a/crates/sovereign-sdk/adapters/risc0-bonsai/Cargo.toml b/crates/sovereign-sdk/adapters/risc0-bonsai/Cargo.toml index e733bb2b2..28e9d0da7 100644 --- a/crates/sovereign-sdk/adapters/risc0-bonsai/Cargo.toml +++ b/crates/sovereign-sdk/adapters/risc0-bonsai/Cargo.toml @@ -22,20 +22,24 @@ 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", version = "0.3" } sov-rollup-interface = { path = "../../rollup-interface", version = "0.3" } tracing = { workspace = true } bonsai-sdk = "0.7.0" hex = { workspace = true } # we are going to use its guest -sov-risc0-adapter = { path = "../risc0" } +sov-risc0-adapter = { path = "../risc0", optional = true } [features] default = [] -native = ["risc0-zkvm/prove", "dep:risc0-zkp", "dep:risc0-circuit-rv32im"] -bench = ["once_cell", "parking_lot", "native", "sov-zk-cycle-utils/native"] +native = [ + "risc0-zkvm/prove", + "dep:risc0-zkp", + "dep:risc0-circuit-rv32im", + "sov-risc0-adapter/native", +] +bench = ["once_cell", "parking_lot", "native"] [[test]] name = "native" diff --git a/crates/sovereign-sdk/adapters/risc0-bonsai/src/host.rs b/crates/sovereign-sdk/adapters/risc0-bonsai/src/host.rs index bfbcb252e..8cad9e1f0 100644 --- a/crates/sovereign-sdk/adapters/risc0-bonsai/src/host.rs +++ b/crates/sovereign-sdk/adapters/risc0-bonsai/src/host.rs @@ -212,27 +212,6 @@ pub struct Risc0BonsaiHost<'a> { last_input_id: Option, } -#[cfg(not(feature = "bench"))] -#[inline(always)] -fn add_benchmarking_callbacks(env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBuilder<'_> { - env -} - -#[cfg(feature = "bench")] -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 -} - 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) -> Self { @@ -313,10 +292,11 @@ impl<'a> ZkvmHost for Risc0BonsaiHost<'a> { /// Proofs are created on the Bonsai API. fn run(&mut self, with_proof: bool) -> Result { if !with_proof { - let env = add_benchmarking_callbacks(ExecutorEnvBuilder::default()) - .write_slice(&self.env) - .build() - .unwrap(); + let env = + sov_risc0_adapter::host::add_benchmarking_callbacks(ExecutorEnvBuilder::default()) + .write_slice(&self.env) + .build() + .unwrap(); let mut executor = ExecutorImpl::from_elf(env, self.elf)?; let session = executor.run()?; diff --git a/crates/sovereign-sdk/adapters/risc0/Cargo.toml b/crates/sovereign-sdk/adapters/risc0/Cargo.toml index 1c1469f36..6971ca62e 100644 --- a/crates/sovereign-sdk/adapters/risc0/Cargo.toml +++ b/crates/sovereign-sdk/adapters/risc0/Cargo.toml @@ -22,13 +22,13 @@ 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", version = "0.3" } +sov-zk-cycle-utils = { path = "../../utils/zk-cycle-utils", version = "0.3", optional = true } sov-rollup-interface = { path = "../../rollup-interface", version = "0.3" } [features] default = [] native = ["risc0-zkvm/prove", "dep:risc0-zkp", "dep:risc0-circuit-rv32im"] -bench = ["once_cell", "parking_lot","native","sov-zk-cycle-utils/native"] +bench = ["once_cell", "parking_lot", "native", "sov-zk-cycle-utils/native"] [[test]] name = "native" diff --git a/crates/sovereign-sdk/adapters/risc0/src/host.rs b/crates/sovereign-sdk/adapters/risc0/src/host.rs index 458e10198..440c351a9 100644 --- a/crates/sovereign-sdk/adapters/risc0/src/host.rs +++ b/crates/sovereign-sdk/adapters/risc0/src/host.rs @@ -18,12 +18,14 @@ pub struct Risc0Host<'a> { #[cfg(not(feature = "bench"))] #[inline(always)] -fn add_benchmarking_callbacks(env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBuilder<'_> { +/// Add benchmarking callbacks to the executor environment. +pub fn add_benchmarking_callbacks(env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBuilder<'_> { env } #[cfg(feature = "bench")] -fn add_benchmarking_callbacks(mut env: ExecutorEnvBuilder<'_>) -> ExecutorEnvBuilder<'_> { +/// 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; diff --git a/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs b/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs index 3dce73233..d8c6cd0c6 100644 --- a/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs +++ b/crates/sovereign-sdk/module-system/sov-modules-stf-blueprint/src/lib.rs @@ -527,7 +527,6 @@ where index_soft_confirmation += 1; - // TODO: check for no da block height jump while index_soft_confirmation < soft_confirmations.len() { // the soft confirmations DA hash must equal to da hash in index_headers // if it's not matching, and if it's not matching the next one, then state transition is invalid. @@ -550,6 +549,11 @@ where current_da_height + 1 ); + assert_eq!( + da_block_headers[index_headers - 1].hash(), + da_block_headers[index_headers].prev_hash() + ); + current_da_height += 1; // if the next one is not matching, then the state transition is invalid.