From 5198c579c9ba8591dce06c73527c31dbdc4af8b9 Mon Sep 17 00:00:00 2001 From: 0xpantera <0xpantera@proton.me> Date: Wed, 4 Feb 2026 10:55:29 +0100 Subject: [PATCH 1/3] feat: add compile-time feature gates for cairo and foundry --- Cargo.toml | 5 +- crates/bargo-core/Cargo.toml | 5 ++ crates/bargo-core/src/backend.rs | 14 ++-- crates/bargo-core/src/backends/mod.rs | 2 + crates/bargo-core/src/cli.rs | 13 ++-- crates/bargo-core/src/commands/clean.rs | 1 + crates/bargo-core/src/commands/doctor.rs | 70 +++++++++++-------- crates/bargo-core/src/commands/evm/backend.rs | 29 ++++++++ crates/bargo-core/src/commands/evm/mod.rs | 9 ++- crates/bargo-core/src/commands/mod.rs | 4 +- crates/bargo-core/src/commands/rebuild.rs | 10 ++- crates/bargo-core/src/config.rs | 2 + crates/bargo-core/src/lib.rs | 24 ++++--- crates/bargo-core/src/util/io.rs | 2 + crates/bargo-core/src/util/paths.rs | 1 + tests/auto_declare.rs | 2 + tests/basic_integration.rs | 8 +++ tests/cairo_integration.rs | 1 + tests/cli_smoke.rs | 52 ++++++++------ tests/error_context.rs | 2 + 20 files changed, 183 insertions(+), 73 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e819182..7f0d62b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ version = "0.2.0" edition = "2024" [dependencies] -bargo-core = { path = "crates/bargo-core" } +bargo-core = { path = "crates/bargo-core", default-features = false } color-eyre = "0.6.5" [dev-dependencies] @@ -19,4 +19,7 @@ assert_fs = "1.1" path-slash = "0.2" [features] +default = ["cairo", "evm-foundry"] +cairo = ["bargo-core/cairo"] +evm-foundry = ["bargo-core/evm-foundry"] runner-history-tests = [] diff --git a/crates/bargo-core/Cargo.toml b/crates/bargo-core/Cargo.toml index a08b216..86ceded 100644 --- a/crates/bargo-core/Cargo.toml +++ b/crates/bargo-core/Cargo.toml @@ -19,3 +19,8 @@ which = "4.4" [dev-dependencies] tempfile = "3.8" + +[features] +default = ["cairo", "evm-foundry"] +cairo = [] +evm-foundry = [] diff --git a/crates/bargo-core/src/backend.rs b/crates/bargo-core/src/backend.rs index 327f4db..5e68021 100644 --- a/crates/bargo-core/src/backend.rs +++ b/crates/bargo-core/src/backend.rs @@ -6,7 +6,10 @@ use color_eyre::Result; -use crate::config::{CairoDeployConfig, Config}; +use crate::config::Config; + +#[cfg(feature = "cairo")] +use crate::config::CairoDeployConfig; /// Trait for polymorphic backend implementations (Cairo, EVM, etc.) /// @@ -43,6 +46,7 @@ pub trait Backend { #[derive(Debug, Clone)] pub enum BackendConfig { /// Cairo/Starknet backend configuration + #[cfg(feature = "cairo")] CairoDeploy(CairoDeployConfig), } @@ -50,6 +54,7 @@ pub enum BackendConfig { #[derive(Debug, Clone, Copy)] pub enum BackendKind { /// Cairo/Starknet backend + #[cfg(feature = "cairo")] Cairo, /// EVM/Ethereum backend Evm, @@ -72,10 +77,9 @@ pub enum BackendKind { /// backend.generate(&config)?; /// ``` pub fn backend_for(backend_kind: BackendKind) -> Box { - use crate::commands::{cairo, evm}; - match backend_kind { - BackendKind::Cairo => Box::new(cairo::backend::CairoBackend::new()), - BackendKind::Evm => Box::new(evm::backend::EvmBackend::new()), + #[cfg(feature = "cairo")] + BackendKind::Cairo => Box::new(crate::commands::cairo::backend::CairoBackend::new()), + BackendKind::Evm => Box::new(crate::commands::evm::backend::EvmBackend::new()), } } diff --git a/crates/bargo-core/src/backends/mod.rs b/crates/bargo-core/src/backends/mod.rs index 22bf859..790a794 100644 --- a/crates/bargo-core/src/backends/mod.rs +++ b/crates/bargo-core/src/backends/mod.rs @@ -9,7 +9,9 @@ //! Legacy direct command execution functions have been removed. pub mod bb; +#[cfg(feature = "evm-foundry")] pub mod foundry; +#[cfg(feature = "cairo")] pub mod garaga; pub mod nargo; diff --git a/crates/bargo-core/src/cli.rs b/crates/bargo-core/src/cli.rs index fca7f19..9fdd470 100644 --- a/crates/bargo-core/src/cli.rs +++ b/crates/bargo-core/src/cli.rs @@ -56,13 +56,14 @@ pub enum Commands { }, /// Cairo/Starknet operations + #[cfg(feature = "cairo")] #[command(about = "Generate Cairo verifiers and interact with Starknet")] Cairo { #[command(subcommand)] command: CairoCommands, }, - /// EVM/Foundry operations + /// EVM operations #[command(about = "Generate Solidity verifiers and interact with EVM networks")] Evm { #[command(subcommand)] @@ -74,6 +75,7 @@ pub enum Commands { Doctor, } +#[cfg(feature = "cairo")] #[derive(Subcommand)] pub enum CairoCommands { /// Generate Cairo verifier contract @@ -117,8 +119,8 @@ pub enum CairoCommands { #[derive(Subcommand)] pub enum EvmCommands { - /// Generate Solidity verifier contract and Foundry project - #[command(about = "Generate Solidity verifier contract with complete Foundry project setup")] + /// Generate Solidity verifier contract + #[command(about = "Generate Solidity verifier contract (Foundry project setup when enabled)")] Gen, /// Generate Keccak oracle proof @@ -130,6 +132,7 @@ pub enum EvmCommands { Verify, /// Deploy verifier contract to EVM network + #[cfg(feature = "evm-foundry")] #[command(about = "Deploy verifier contract using Foundry")] Deploy { /// Network to deploy to (mainnet or sepolia) @@ -138,10 +141,11 @@ pub enum EvmCommands { }, /// Generate calldata for proof verification - #[command(about = "Generate calldata for proof verification using cast")] + #[command(about = "Generate calldata for proof verification")] Calldata, /// Verify proof on-chain + #[cfg(feature = "evm-foundry")] #[command(about = "Verify proof on EVM network using deployed verifier")] VerifyOnchain, } @@ -151,6 +155,7 @@ pub enum Backend { /// Barretenberg backend (EVM/Solidity) Bb, /// Starknet backend (Cairo) + #[cfg(feature = "cairo")] Starknet, /// All backends All, diff --git a/crates/bargo-core/src/commands/clean.rs b/crates/bargo-core/src/commands/clean.rs index 52e2f47..9da63d8 100644 --- a/crates/bargo-core/src/commands/clean.rs +++ b/crates/bargo-core/src/commands/clean.rs @@ -44,6 +44,7 @@ pub fn run(cfg: &Config, backend: Backend) -> Result<()> { println!("{}", info_msg("target/bb/ already clean")); } } + #[cfg(feature = "cairo")] Backend::Starknet => { if cfg.dry_run { println!("Would run: rm -rf target/starknet/"); diff --git a/crates/bargo-core/src/commands/doctor.rs b/crates/bargo-core/src/commands/doctor.rs index 8acb6c7..af087b9 100644 --- a/crates/bargo-core/src/commands/doctor.rs +++ b/crates/bargo-core/src/commands/doctor.rs @@ -41,47 +41,53 @@ pub fn run(cfg: &Config) -> Result<()> { } } - match which::which("garaga") { - Ok(path) => { - if !cfg.quiet { - println!("✅ garaga: {}", path.display()); + #[cfg(feature = "cairo")] + { + match which::which("garaga") { + Ok(path) => { + if !cfg.quiet { + println!("✅ garaga: {}", path.display()); + } } - } - Err(_) => { - if !cfg.quiet { - println!("⚠️ garaga: not found (optional - needed for Cairo features)"); - println!(" Install with: pipx install garaga"); - println!(" Requires Python 3.10+"); + Err(_) => { + if !cfg.quiet { + println!("⚠️ garaga: not found (optional - needed for Cairo features)"); + println!(" Install with: pipx install garaga"); + println!(" Requires Python 3.10+"); + } } } } - match which::which("forge") { - Ok(path) => { - if !cfg.quiet { - println!("✅ forge: {}", path.display()); + #[cfg(feature = "evm-foundry")] + { + match which::which("forge") { + Ok(path) => { + if !cfg.quiet { + println!("✅ forge: {}", path.display()); + } } - } - Err(_) => { - if !cfg.quiet { - println!("⚠️ forge: not found (optional - needed for EVM features)"); - println!(" Install with: curl -L https://foundry.paradigm.xyz | bash"); - println!(" Then run: foundryup"); + Err(_) => { + if !cfg.quiet { + println!("⚠️ forge: not found (optional - needed for EVM deploy features)"); + println!(" Install with: curl -L https://foundry.paradigm.xyz | bash"); + println!(" Then run: foundryup"); + } } } - } - match which::which("cast") { - Ok(path) => { - if !cfg.quiet { - println!("✅ cast: {}", path.display()); + match which::which("cast") { + Ok(path) => { + if !cfg.quiet { + println!("✅ cast: {}", path.display()); + } } - } - Err(_) => { - if !cfg.quiet { - println!("⚠️ cast: not found (optional - needed for EVM features)"); - println!(" Install with: curl -L https://foundry.paradigm.xyz | bash"); - println!(" Then run: foundryup"); + Err(_) => { + if !cfg.quiet { + println!("⚠️ cast: not found (optional - used for on-chain helpers)"); + println!(" Install with: curl -L https://foundry.paradigm.xyz | bash"); + println!(" Then run: foundryup"); + } } } } @@ -94,7 +100,9 @@ pub fn run(cfg: &Config) -> Result<()> { } else { println!("🚨 Some required dependencies are missing."); println!(" Core features require: nargo + bb"); + #[cfg(feature = "evm-foundry")] println!(" EVM deployment features also require: forge + cast"); + #[cfg(feature = "cairo")] println!(" Cairo features also require: garaga"); } } diff --git a/crates/bargo-core/src/commands/evm/backend.rs b/crates/bargo-core/src/commands/evm/backend.rs index 02b903d..e43c46c 100644 --- a/crates/bargo-core/src/commands/evm/backend.rs +++ b/crates/bargo-core/src/commands/evm/backend.rs @@ -10,6 +10,9 @@ use crate::{ config::Config, }; +#[cfg(not(feature = "evm-foundry"))] +use crate::util::create_smart_error; + use super::workflow; /// EVM backend implementation for Ethereum-based proof systems @@ -45,6 +48,7 @@ impl Backend for EvmBackend { } /// Deploy Solidity verifier contract to EVM network + #[cfg(feature = "evm-foundry")] fn deploy(&mut self, cfg: &Config, network: Option<&str>) -> Result<()> { // Use provided network or default to "sepolia" let network_str = network.unwrap_or("sepolia"); @@ -52,11 +56,36 @@ impl Backend for EvmBackend { } /// Verify proof on-chain using deployed EVM verifier + #[cfg(feature = "evm-foundry")] fn verify_onchain(&mut self, cfg: &Config, _address: Option<&str>) -> Result<()> { // EVM verify_onchain doesn't take an address parameter in the current implementation workflow::run_verify_onchain(cfg) } + /// Deploy Solidity verifier contract to EVM network + #[cfg(not(feature = "evm-foundry"))] + fn deploy(&mut self, _cfg: &Config, _network: Option<&str>) -> Result<()> { + Err(create_smart_error( + "EVM deployment is not available in this build", + &[ + "Rebuild with the evm-foundry feature enabled to use deploy commands", + "Example: cargo build --features evm-foundry", + ], + )) + } + + /// Verify proof on-chain using deployed EVM verifier + #[cfg(not(feature = "evm-foundry"))] + fn verify_onchain(&mut self, _cfg: &Config, _address: Option<&str>) -> Result<()> { + Err(create_smart_error( + "EVM on-chain verification is not available in this build", + &[ + "Rebuild with the evm-foundry feature enabled to use verify-onchain", + "Example: cargo build --features evm-foundry", + ], + )) + } + /// Configure backend with backend-specific settings fn configure(&mut self, _config: BackendConfig) -> Result<()> { // EVM backend currently doesn't need any configuration diff --git a/crates/bargo-core/src/commands/evm/mod.rs b/crates/bargo-core/src/commands/evm/mod.rs index c273e31..4030b25 100644 --- a/crates/bargo-core/src/commands/evm/mod.rs +++ b/crates/bargo-core/src/commands/evm/mod.rs @@ -7,11 +7,16 @@ pub mod backend; pub mod bb_operations; pub mod directories; pub mod error; -pub mod foundry; pub mod workflow; +#[cfg(feature = "evm-foundry")] +pub mod foundry; + // Re-export main workflow functions for use by main.rs -pub use workflow::{run_calldata, run_deploy, run_gen, run_prove, run_verify, run_verify_onchain}; +pub use workflow::{run_calldata, run_gen, run_prove, run_verify}; + +#[cfg(feature = "evm-foundry")] +pub use workflow::{run_deploy, run_verify_onchain}; // Re-export error types for convenience pub use error::{EvmError, Result}; diff --git a/crates/bargo-core/src/commands/mod.rs b/crates/bargo-core/src/commands/mod.rs index 15b4382..4786ba6 100644 --- a/crates/bargo-core/src/commands/mod.rs +++ b/crates/bargo-core/src/commands/mod.rs @@ -1,5 +1,4 @@ pub mod build; -pub mod cairo; pub mod evm; pub mod check; @@ -8,4 +7,7 @@ pub mod rebuild; pub mod doctor; pub mod common; +#[cfg(feature = "cairo")] +pub mod cairo; + pub use common::build_nargo_args; diff --git a/crates/bargo-core/src/commands/rebuild.rs b/crates/bargo-core/src/commands/rebuild.rs index 89dcf2d..f87628f 100644 --- a/crates/bargo-core/src/commands/rebuild.rs +++ b/crates/bargo-core/src/commands/rebuild.rs @@ -23,7 +23,15 @@ pub fn run(cfg: &Config, backend: Backend) -> Result<()> { } clean::run(cfg, backend)?; - if backend != Backend::Starknet { + #[cfg(feature = "cairo")] + { + if backend != Backend::Starknet { + summary.add_operation("Build artifacts cleaned"); + } + } + + #[cfg(not(feature = "cairo"))] + { summary.add_operation("Build artifacts cleaned"); } diff --git a/crates/bargo-core/src/config.rs b/crates/bargo-core/src/config.rs index 35ccbed..a5b6218 100644 --- a/crates/bargo-core/src/config.rs +++ b/crates/bargo-core/src/config.rs @@ -13,6 +13,7 @@ pub struct Config { } /// Configuration specific to Cairo deploy operations +#[cfg(feature = "cairo")] #[derive(Clone, Debug)] pub struct CairoDeployConfig { pub class_hash: Option, @@ -20,6 +21,7 @@ pub struct CairoDeployConfig { pub no_declare: bool, } +#[cfg(feature = "cairo")] impl CairoDeployConfig { pub fn new(class_hash: Option, auto_declare: bool, no_declare: bool) -> Self { Self { diff --git a/crates/bargo-core/src/lib.rs b/crates/bargo-core/src/lib.rs index 67e786b..58833e1 100644 --- a/crates/bargo-core/src/lib.rs +++ b/crates/bargo-core/src/lib.rs @@ -11,7 +11,12 @@ pub mod commands; pub mod config; pub mod runner; -use backend::{BackendConfig, BackendKind, backend_for}; +use backend::{BackendKind, backend_for}; + +#[cfg(feature = "cairo")] +use backend::BackendConfig; + +#[cfg(feature = "cairo")] use config::CairoDeployConfig; pub use cli::Cli; @@ -42,7 +47,7 @@ pub fn run() -> Result<()> { } fn dispatch(cli: &Cli, cfg: &Config) -> Result<()> { - use cli::{Backend, CairoCommands, Commands, EvmCommands}; + use cli::{Backend, Commands, EvmCommands}; use util::print_banner; match &cli.command { @@ -70,29 +75,30 @@ fn dispatch(cli: &Cli, cfg: &Config) -> Result<()> { } commands::rebuild::run(cfg, backend.unwrap_or(Backend::All)) } + #[cfg(feature = "cairo")] Commands::Cairo { command } => match command { - CairoCommands::Gen => { + cli::CairoCommands::Gen => { if !cfg.quiet { print_banner("cairo gen"); } let mut backend = backend_for(BackendKind::Cairo); backend.generate(cfg) } - CairoCommands::Prove => { + cli::CairoCommands::Prove => { if !cfg.quiet { print_banner("cairo prove"); } let mut backend = backend_for(BackendKind::Cairo); backend.prove(cfg) } - CairoCommands::Verify => { + cli::CairoCommands::Verify => { if !cfg.quiet { print_banner("cairo verify"); } let mut backend = backend_for(BackendKind::Cairo); backend.verify(cfg) } - CairoCommands::Calldata => { + cli::CairoCommands::Calldata => { if !cfg.quiet { print_banner("cairo calldata"); } @@ -100,7 +106,7 @@ fn dispatch(cli: &Cli, cfg: &Config) -> Result<()> { backend.calldata(cfg) } - CairoCommands::Deploy { + cli::CairoCommands::Deploy { class_hash, auto_declare, no_declare, @@ -117,7 +123,7 @@ fn dispatch(cli: &Cli, cfg: &Config) -> Result<()> { backend.deploy(cfg, None) } - CairoCommands::VerifyOnchain { address } => { + cli::CairoCommands::VerifyOnchain { address } => { if !cfg.quiet { print_banner("cairo verify-onchain"); } @@ -147,6 +153,7 @@ fn dispatch(cli: &Cli, cfg: &Config) -> Result<()> { let mut backend = backend_for(BackendKind::Evm); backend.verify(cfg) } + #[cfg(feature = "evm-foundry")] EvmCommands::Deploy { network } => { if !cfg.quiet { print_banner("evm deploy"); @@ -161,6 +168,7 @@ fn dispatch(cli: &Cli, cfg: &Config) -> Result<()> { let mut backend = backend_for(BackendKind::Evm); backend.calldata(cfg) } + #[cfg(feature = "evm-foundry")] EvmCommands::VerifyOnchain => { if !cfg.quiet { print_banner("evm verify-onchain"); diff --git a/crates/bargo-core/src/util/io.rs b/crates/bargo-core/src/util/io.rs index 1f89100..25f98bf 100644 --- a/crates/bargo-core/src/util/io.rs +++ b/crates/bargo-core/src/util/io.rs @@ -307,6 +307,7 @@ pub fn ensure_contracts_dir() -> Result<()> { /// - If destination exists, it will be removed first /// - Creates parent directories of destination if needed /// - Moves the entire directory tree +#[cfg(feature = "cairo")] pub fn move_generated_project(from: &str, to: &str) -> Result<()> { let source_path = Path::new(from); let dest_path = Path::new(to); @@ -464,6 +465,7 @@ mod tests { ); } + #[cfg(feature = "cairo")] #[test] fn test_move_generated_project() { let temp_dir = tempdir().unwrap(); diff --git a/crates/bargo-core/src/util/paths.rs b/crates/bargo-core/src/util/paths.rs index 5400ddb..1666598 100644 --- a/crates/bargo-core/src/util/paths.rs +++ b/crates/bargo-core/src/util/paths.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use tracing::{debug, warn}; /// Backend flavour for artifact generation +#[cfg_attr(not(feature = "cairo"), allow(dead_code))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Flavour { /// Barretenberg backend (shared base artifacts) diff --git a/tests/auto_declare.rs b/tests/auto_declare.rs index 5b5708c..a1eed7a 100644 --- a/tests/auto_declare.rs +++ b/tests/auto_declare.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "cairo")] + use assert_cmd::Command; #[test] diff --git a/tests/basic_integration.rs b/tests/basic_integration.rs index 6a6d36d..90e7906 100644 --- a/tests/basic_integration.rs +++ b/tests/basic_integration.rs @@ -95,7 +95,10 @@ fn test_bargo_help() { assert!(stdout.contains("bargo consolidates nargo")); assert!(stdout.contains("Commands:")); assert!(stdout.contains("build")); + #[cfg(feature = "cairo")] assert!(stdout.contains("cairo")); + #[cfg(not(feature = "cairo"))] + assert!(!stdout.contains("cairo")); assert!(stdout.contains("evm")); assert!(stdout.contains("check")); assert!(stdout.contains("clean")); @@ -162,9 +165,13 @@ fn test_evm_help() { assert!(stdout.contains("prove")); assert!(stdout.contains("verify")); assert!(stdout.contains("gen")); + #[cfg(feature = "evm-foundry")] assert!(stdout.contains("deploy")); + #[cfg(not(feature = "evm-foundry"))] + assert!(!stdout.contains("deploy")); } +#[cfg(feature = "cairo")] #[test] fn test_cairo_help() { let output = run_bargo_global(&["cairo", "--help"]); @@ -263,6 +270,7 @@ fn test_evm_prove_dry_run() { } } +#[cfg(feature = "cairo")] #[test] fn test_cairo_prove_dry_run() { let (_temp_dir, project_dir) = create_test_project(); diff --git a/tests/cairo_integration.rs b/tests/cairo_integration.rs index affd721..d32c556 100644 --- a/tests/cairo_integration.rs +++ b/tests/cairo_integration.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "cairo")] //! Integration tests for bargo cairo commands //! //! These tests use DryRunRunner to verify cairo workflow execution without running external tools, diff --git a/tests/cli_smoke.rs b/tests/cli_smoke.rs index 7575792..529ad18 100644 --- a/tests/cli_smoke.rs +++ b/tests/cli_smoke.rs @@ -103,6 +103,7 @@ fn rebuild_command_pkg_flag_propagated() { .stdout(contains("--package my_pkg")); } +#[cfg(feature = "cairo")] #[test] fn cairo_gen_dry_run() { // Test that Cairo gen command works through the new BackendTrait system @@ -113,6 +114,7 @@ fn cairo_gen_dry_run() { .success(); } +#[cfg(feature = "cairo")] #[test] fn cairo_gen_pkg_flag_propagated() { // Test that package flag propagation works through the BackendTrait system @@ -147,14 +149,17 @@ fn evm_gen_pkg_flag_propagated() { fn trait_system_generates_expected_output() { use predicates::str::contains; - // Verify that Cairo gen through trait system produces expected dry-run output - Command::cargo_bin("bargo") - .unwrap() - .args(["--dry-run", "--pkg", "test_pkg", "cairo", "gen"]) - .assert() - .stdout(contains("Would run: bb prove")) - .stdout(contains("Would run: bb write_vk")) - .stdout(contains("Would run: garaga gen")); + #[cfg(feature = "cairo")] + { + // Verify that Cairo gen through trait system produces expected dry-run output + Command::cargo_bin("bargo") + .unwrap() + .args(["--dry-run", "--pkg", "test_pkg", "cairo", "gen"]) + .assert() + .stdout(contains("Would run: bb prove")) + .stdout(contains("Would run: bb write_vk")) + .stdout(contains("Would run: garaga gen")); + } // Verify that EVM gen through trait system produces expected dry-run output Command::cargo_bin("bargo") @@ -165,6 +170,7 @@ fn trait_system_generates_expected_output() { .stdout(contains("Would run: bb write_vk")); } +#[cfg(feature = "cairo")] #[test] fn cairo_prove_through_trait_system() { // Test that Cairo prove works through the trait system @@ -175,6 +181,7 @@ fn cairo_prove_through_trait_system() { .success(); } +#[cfg(feature = "cairo")] #[test] fn cairo_verify_through_trait_system() { // Test that Cairo verify works through the trait system @@ -185,6 +192,7 @@ fn cairo_verify_through_trait_system() { .success(); } +#[cfg(feature = "cairo")] #[test] fn cairo_calldata_through_trait_system() { // Test that Cairo calldata works through the trait system @@ -198,6 +206,7 @@ fn cairo_calldata_through_trait_system() { // Note: cairo deploy test skipped due to workflow validation issues // The underlying workflow checks for Cairo contract directory before dry-run mode +#[cfg(feature = "cairo")] #[test] fn cairo_verify_onchain_through_trait_system() { // Test that Cairo verify-onchain works through the trait system @@ -256,18 +265,21 @@ fn evm_calldata_through_trait_system() { #[test] fn all_trait_workflows_preserve_pkg_flag() { // Test a few key workflows to ensure --pkg flag propagation still works - Command::cargo_bin("bargo") - .unwrap() - .args([ - "--verbose", - "--dry-run", - "--pkg", - "my_test_pkg", - "cairo", - "prove", - ]) - .assert() - .success(); + #[cfg(feature = "cairo")] + { + Command::cargo_bin("bargo") + .unwrap() + .args([ + "--verbose", + "--dry-run", + "--pkg", + "my_test_pkg", + "cairo", + "prove", + ]) + .assert() + .success(); + } Command::cargo_bin("bargo") .unwrap() diff --git a/tests/error_context.rs b/tests/error_context.rs index 71db028..2dc4a1d 100644 --- a/tests/error_context.rs +++ b/tests/error_context.rs @@ -100,6 +100,7 @@ fn test_missing_project_error_context() { } /// Test that missing artifact errors include proper context +#[cfg(feature = "cairo")] #[test] fn test_missing_artifacts_error_context() { color_eyre::install().ok(); @@ -167,6 +168,7 @@ fn test_file_operation_error_context() { } /// Test that missing proof artifacts errors include proper context +#[cfg(feature = "cairo")] #[test] fn test_missing_proof_artifacts_error_context() { color_eyre::install().ok(); From c10dc0a09f307ddc715116ef6e3a33b00e4ea2f0 Mon Sep 17 00:00:00 2001 From: 0xpantera <0xpantera@proton.me> Date: Wed, 4 Feb 2026 10:55:45 +0100 Subject: [PATCH 2/3] fix: update evm bb cli flow and calldata formatting --- .../src/commands/evm/bb_operations.rs | 28 ++- .../bargo-core/src/commands/evm/workflow.rs | 169 ++++++++++++------ 2 files changed, 122 insertions(+), 75 deletions(-) diff --git a/crates/bargo-core/src/commands/evm/bb_operations.rs b/crates/bargo-core/src/commands/evm/bb_operations.rs index 37c0d88..c0dec50 100644 --- a/crates/bargo-core/src/commands/evm/bb_operations.rs +++ b/crates/bargo-core/src/commands/evm/bb_operations.rs @@ -11,11 +11,7 @@ use crate::{ util::{self, Flavour}, }; -/// Generate an EVM-compatible proof using BB with keccak oracle hash -/// -/// This function generates a proof with the following BB flags: -/// - `--oracle_hash keccak` -/// - `--output_format bytes_and_fields` +/// Generate an EVM-compatible proof using BB /// /// # Arguments /// * `cfg` - Configuration containing runner and flags @@ -26,6 +22,7 @@ use crate::{ pub fn generate_evm_proof(cfg: &Config, pkg: &str) -> Result<()> { let bytecode = util::get_bytecode_path(pkg, Flavour::Bb); let witness = util::get_witness_path(pkg, Flavour::Bb); + let vk_path = util::get_vk_path(Flavour::Evm); common::run_tool( cfg, @@ -38,19 +35,16 @@ pub fn generate_evm_proof(cfg: &Config, pkg: &str) -> Result<()> { &witness.to_string_lossy(), "-o", "./target/evm/", - "--oracle_hash", - "keccak", - "--output_format", - "bytes_and_fields", + "-k", + &vk_path.to_string_lossy(), + "-t", + "evm", ], ) } /// Generate an EVM-compatible verification key using BB /// -/// This function generates a VK with the following BB flags: -/// - `--oracle_hash keccak` -/// /// # Arguments /// * `cfg` - Configuration containing runner and flags /// * `pkg` - Package name for locating bytecode file @@ -65,12 +59,12 @@ pub fn generate_evm_vk(cfg: &Config, pkg: &str) -> Result<()> { "bb", &[ "write_vk", - "--oracle_hash", - "keccak", "-b", &bytecode.to_string_lossy(), "-o", "./target/evm/", + "-t", + "evm", ], ) } @@ -102,8 +96,8 @@ pub fn verify_evm_proof(cfg: &Config, _pkg: &str) -> Result<()> { &vk_path.to_string_lossy(), "-i", &public_inputs_path.to_string_lossy(), - "--oracle_hash", - "keccak", + "-t", + "evm", ], ) } @@ -120,8 +114,8 @@ pub fn verify_evm_proof(cfg: &Config, _pkg: &str) -> Result<()> { /// # Returns /// * `Result<()>` - Success or error from either operation pub fn generate_evm_proof_and_vk(cfg: &Config, pkg: &str) -> Result<()> { - generate_evm_proof(cfg, pkg)?; generate_evm_vk(cfg, pkg)?; + generate_evm_proof(cfg, pkg)?; Ok(()) } diff --git a/crates/bargo-core/src/commands/evm/workflow.rs b/crates/bargo-core/src/commands/evm/workflow.rs index 77d9e05..14f8c3e 100644 --- a/crates/bargo-core/src/commands/evm/workflow.rs +++ b/crates/bargo-core/src/commands/evm/workflow.rs @@ -5,6 +5,7 @@ use color_eyre::Result; use color_eyre::eyre::WrapErr; +use serde_json::json; use tracing::info; use crate::{ @@ -15,7 +16,10 @@ use crate::{ }, }; -use super::{bb_operations, directories, foundry, load_env_vars}; +use super::{bb_operations, directories, load_env_vars}; + +#[cfg(feature = "evm-foundry")] +use super::foundry; /// Run the EVM gen workflow /// @@ -51,30 +55,75 @@ pub fn run_gen(cfg: &Config) -> Result<()> { let mut summary = OperationSummary::new(); - // Step 1: Initialize Foundry project + // Step 1: Initialize project structure if cfg.verbose { - info!("Initializing Foundry project"); + info!("Initializing EVM project structure"); } let foundry_timer = Timer::start(); - foundry::init_default_foundry_project(cfg).map_err(enhance_error_with_suggestions)?; + #[cfg(feature = "evm-foundry")] + { + foundry::init_default_foundry_project(cfg).map_err(enhance_error_with_suggestions)?; + + if !cfg.quiet { + let foundry_dir = directories::get_evm_contracts_dir(); + println!( + "{}", + success(&format_operation_result( + "Foundry project initialized", + &foundry_dir, + &foundry_timer + )) + ); + summary.add_operation("Foundry project structure"); + } + } + + #[cfg(not(feature = "evm-foundry"))] + { + directories::ensure_evm_contracts_dir().map_err(enhance_error_with_suggestions)?; + directories::ensure_evm_contracts_src_dir().map_err(enhance_error_with_suggestions)?; + + if !cfg.quiet { + let contracts_dir = directories::get_evm_contracts_dir(); + println!( + "{}", + success(&format_operation_result( + "EVM contracts directory prepared", + &contracts_dir, + &foundry_timer + )) + ); + summary.add_operation("EVM contracts directory"); + } + } + + // Step 2: Generate EVM VK + if cfg.verbose { + info!("Generating EVM verification key"); + } + let vk_timer = Timer::start(); + bb_operations::generate_evm_vk(cfg, &pkg_name).map_err(enhance_error_with_suggestions)?; if !cfg.quiet { - let foundry_dir = directories::get_evm_contracts_dir(); + let vk_path = util::get_vk_path(Flavour::Evm); println!( "{}", success(&format_operation_result( - "Foundry project initialized", - &foundry_dir, - &foundry_timer + "EVM VK generated", + &vk_path, + &vk_timer )) ); - summary.add_operation("Foundry project structure"); + summary.add_operation(&format!( + "Verification key ({})", + util::format_file_size(&vk_path) + )); } - // Step 2: Generate EVM proof + // Step 3: Generate EVM proof if cfg.verbose { - info!("Generating EVM proof with keccak oracle"); + info!("Generating EVM proof"); } let proof_timer = Timer::start(); bb_operations::generate_evm_proof(cfg, &pkg_name).map_err(enhance_error_with_suggestions)?; @@ -95,29 +144,6 @@ pub fn run_gen(cfg: &Config) -> Result<()> { )); } - // Step 3: Generate EVM VK - if cfg.verbose { - info!("Generating EVM verification key"); - } - let vk_timer = Timer::start(); - bb_operations::generate_evm_vk(cfg, &pkg_name).map_err(enhance_error_with_suggestions)?; - - if !cfg.quiet { - let vk_path = util::get_vk_path(Flavour::Evm); - println!( - "{}", - success(&format_operation_result( - "EVM VK generated", - &vk_path, - &vk_timer - )) - ); - summary.add_operation(&format!( - "Verification key ({})", - util::format_file_size(&vk_path) - )); - } - // Step 4: Generate Solidity verifier contract if cfg.verbose { info!("Generating Solidity verifier contract"); @@ -144,6 +170,7 @@ pub fn run_gen(cfg: &Config) -> Result<()> { println!(); println!("🎯 Next steps:"); println!(" • Generate calldata: bargo evm calldata"); + #[cfg(feature = "evm-foundry")] println!(" • Deploy contract: bargo evm deploy --network "); } @@ -234,6 +261,7 @@ pub fn run_verify(cfg: &Config) -> Result<()> { /// /// # Returns /// * `Result<()>` - Success or error from workflow +#[cfg(feature = "evm-foundry")] pub fn run_deploy(cfg: &Config, network: &str) -> Result<()> { load_env_vars(); @@ -334,26 +362,18 @@ pub fn run_deploy(cfg: &Config, network: &str) -> Result<()> { pub fn run_calldata(cfg: &Config) -> Result<()> { load_env_vars(); - // Validate Foundry installation + // Check that proof and public inputs exist (BB output for EVM) + let proof_path = util::get_proof_path(Flavour::Evm); + let public_inputs_path = util::get_public_inputs_path(Flavour::Evm); if !cfg.dry_run { - foundry::validate_foundry_installation().map_err(enhance_error_with_suggestions)?; - } - - // Check that proof fields JSON exists (BB output for EVM) - let proof_fields_path = std::path::PathBuf::from("./target/evm/proof_fields.json"); - if !cfg.dry_run && !proof_fields_path.exists() { - return Err(create_smart_error( - "Proof fields file not found", - &[ - "Run 'bargo build' and 'bargo evm prove' first to generate proof files", - "Ensure the target/evm/proof_fields.json file exists", - ], - )); + util::validate_files_exist(&[proof_path.clone(), public_inputs_path.clone()]) + .map_err(enhance_error_with_suggestions)?; } if cfg.dry_run { - println!("Would generate calldata from proof fields JSON"); - println!("Would read: {}", proof_fields_path.display()); + println!("Would generate calldata from proof and public inputs"); + println!("Would read: {}", proof_path.display()); + println!("Would read: {}", public_inputs_path.display()); return Ok(()); } @@ -361,14 +381,43 @@ pub fn run_calldata(cfg: &Config) -> Result<()> { info!("Generating calldata for EVM proof verification"); } - // Read proof fields and format for contract call - let proof_fields_content = std::fs::read_to_string(&proof_fields_path) - .wrap_err_with(|| format!("reading proof fields file {}", proof_fields_path.display()))?; + // Read proof and public inputs (binary) and format for contract call + let proof_bytes = std::fs::read(&proof_path) + .wrap_err_with(|| format!("reading proof file {}", proof_path.display()))?; + let public_inputs = std::fs::read(&public_inputs_path).wrap_err_with(|| { + format!( + "reading public inputs file {}", + public_inputs_path.display() + ) + })?; + + if public_inputs.len() % 32 != 0 { + return Err(create_smart_error( + "Public inputs length is not a multiple of 32 bytes", + &[ + "Ensure the proof was generated for the EVM verifier target", + "Re-run: bargo evm prove", + ], + )); + } + + let public_inputs_hex: Vec = public_inputs + .chunks(32) + .map(|chunk| format!("0x{}", hex::encode(chunk))) + .collect(); + + let calldata = json!({ + "proof": format!("0x{}", hex::encode(proof_bytes)), + "public_inputs": public_inputs_hex, + }); // Save formatted calldata let calldata_path = std::path::PathBuf::from("./target/evm/calldata.json"); - std::fs::write(&calldata_path, &proof_fields_content) - .wrap_err_with(|| format!("writing calldata to {}", calldata_path.display()))?; + std::fs::write( + &calldata_path, + serde_json::to_vec_pretty(&calldata).unwrap(), + ) + .wrap_err_with(|| format!("writing calldata to {}", calldata_path.display()))?; if !cfg.quiet { let calldata_timer = Timer::start(); @@ -387,9 +436,12 @@ pub fn run_calldata(cfg: &Config) -> Result<()> { util::format_file_size(&calldata_path) )); summary.print(); - println!(); - println!("🎯 Next step:"); - println!(" • Verify on-chain: bargo evm verify-onchain"); + #[cfg(feature = "evm-foundry")] + { + println!(); + println!("🎯 Next step:"); + println!(" • Verify on-chain: bargo evm verify-onchain"); + } } Ok(()) @@ -402,6 +454,7 @@ pub fn run_calldata(cfg: &Config) -> Result<()> { /// /// # Returns /// * `Result<()>` - Success or error from workflow +#[cfg(feature = "evm-foundry")] pub fn run_verify_onchain(cfg: &Config) -> Result<()> { load_env_vars(); From e96a32622ce93fa947d7b80996c00a075935ee60 Mon Sep 17 00:00:00 2001 From: 0xpantera <0xpantera@proton.me> Date: Wed, 4 Feb 2026 10:55:57 +0100 Subject: [PATCH 3/3] docs: refresh README for feature flags and bb cli --- README.md | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 3e210b1..70e8f72 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ bargo is a Swiss Army knife for circuit proving, verification, smart contract ge - **[nargo](https://noir-lang.org/docs/getting_started/installation/)** - Noir language toolchain - **[bb](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg)** - Barretenberg proving system -### EVM Workflow (Optional) +### EVM Foundry Workflow (Optional, `evm-foundry` feature) - **[Foundry](https://getfoundry.sh/)** - For Solidity contract deployment - **Environment Variables**: `RPC_URL`, `PRIVATE_KEY` in `.env` file -### Starknet Workflow (Optional) +### Starknet Workflow (Optional, `cairo` feature) - **[starkli](https://github.com/xJonathanLEI/starkli)** - Starknet CLI tool - **[garaga](https://github.com/keep-starknet-strange/garaga)** - Cairo verifier generation - **Python 3.10+** and **pipx** for garaga installation @@ -41,12 +41,12 @@ Currently, Noir developers must juggle multiple tools and remember complex comma # Current workflow (verbose and error-prone) nargo check nargo execute # produce bytecode + witness -bb prove -b target/foo.json -w target/foo.gz -o target/ -bb write_vk -b target/foo.json -o target/ -bb verify -k target/vk -p target/proof +bb write_vk -b target/foo.json -o target/ -t evm +bb prove -b target/foo.json -w target/foo.gz -o target/ -t evm -k target/vk +bb verify -k target/vk -p target/proof -i target/public_inputs -t evm # Plus remembering different flags for Solidity generation -bb write_vk --oracle_hash keccak -b target/foo.json -o target/ +bb write_vk -b target/foo.json -o target/ -t evm bb write_solidity_verifier -k target/vk -o contracts/Verifier.sol # And for Starknet verifier contracts: @@ -72,12 +72,12 @@ bargo evm verify bargo evm gen bargo evm calldata -# Starknet Workflow +# Starknet Workflow (requires `cairo` feature) bargo build bargo cairo prove bargo cairo verify bargo cairo gen -bargo cairo data +bargo cairo calldata ``` **How bargo improves on underlying tools:** @@ -96,15 +96,17 @@ bargo cairo data - `bargo rebuild` - Clean and rebuild from scratch - `bargo doctor` - Check that all required tools are installed -### EVM Commands +### EVM Commands (Core) - `bargo evm prove` - Generate proof and verification key with Keccak oracle - `bargo evm verify` - Verify proof locally -- `bargo evm gen` - Generate Solidity verifier contract and Foundry project +- `bargo evm gen` - Generate Solidity verifier contract (Foundry project when enabled) - `bargo evm calldata` - Generate calldata for on-chain verification + +### EVM Commands (Foundry, `evm-foundry` feature) - `bargo evm deploy` - Deploy verifier contract to EVM networks - `bargo evm verify-onchain` - Verify proof on-chain -### Starknet Commands +### Starknet Commands (`cairo` feature) - `bargo cairo prove` - Generate proof and verification key with Starknet oracle - `bargo cairo verify` - Verify proof locally - `bargo cairo gen` - Generate Cairo verifier contract using garaga @@ -132,6 +134,22 @@ bargo --help bargo doctor # Check dependencies ``` +### Feature Flags + +By default, `bargo` enables both the Cairo and Foundry feature sets. To disable them at compile time: + +```toml +# Cargo.toml dependency example +bargo = { version = "0.2.0", default-features = false, features = ["cairo"] } +``` + +```bash +# Building locally +cargo build --no-default-features +cargo build --no-default-features --features cairo +cargo build --no-default-features --features evm-foundry +``` + ### EVM Setup (Optional) ```bash @@ -265,7 +283,7 @@ target/ └── calldata.json contracts/ -├── evm/ # Foundry project with Solidity verifier +├── evm/ # Solidity verifier (Foundry project when enabled) └── cairo/ # Cairo verifier project ``` @@ -324,13 +342,13 @@ Error: Command execution failed: bb prove --scheme ultra_honk ### Backend-Specific Errors -#### Cairo Backend Errors +#### Cairo Backend Errors (requires `cairo` feature) - **Deploy failures**: Issues with Starknet contract deployment - **Class hash errors**: Problems with contract declaration - **Garaga integration**: Tool-specific failures during contract generation #### EVM Backend Errors -- **Foundry integration**: Issues with Solidity compilation or deployment +- **Foundry integration**: Issues with Solidity compilation or deployment (requires `evm-foundry`) - **Network errors**: Problems connecting to Ethereum networks - **Contract compilation**: Solidity verifier generation failures @@ -365,8 +383,8 @@ Error: Command execution failed: bb prove --scheme ultra_honk |---------|----------| | "nargo not found" | Install Noir toolchain: `curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash` | | "bb not found" | Install Barretenberg: Follow Aztec installation docs | -| "garaga not found" | Install with pip: `pip install garaga==0.18.1` | -| "forge not found" | Install Foundry: `curl -L https://foundry.paradigm.xyz \| bash && foundryup` | +| "garaga not found" | Install with pip: `pip install garaga==0.18.1` (only for `cairo`) | +| "forge not found" | Install Foundry: `curl -L https://foundry.paradigm.xyz \| bash && foundryup` (only for `evm-foundry`) | | Version compatibility issues | Use `bargo doctor` to check versions and compatibility | | Missing artifacts | Run prerequisite commands: `bargo build` → `bargo prove` | | Network connection issues | Check RPC URLs and network configuration in `.env` | @@ -446,4 +464,4 @@ Integration tests use `ScopedDir` guards to prevent race conditions when running ## License -MIT License - see [LICENSE](LICENSE) file for details. \ No newline at end of file +MIT License - see [LICENSE](LICENSE) file for details.