From 38d3bd995afc7010738590e60bd4998a1a0e2cf5 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Wed, 19 Nov 2025 05:26:10 +0500 Subject: [PATCH 01/10] feat: switch to boundless market --- crates/cli/src/program.rs | 4 + crates/config/src/app_config.rs | 37 +- crates/support-scripts/src/lib.rs | 9 + crates/support-scripts/src/program_risc0.rs | 21 +- crates/support/Cargo.lock | 1859 ++++++++++++++++++- crates/support/Cargo.toml | 4 + crates/support/app/src/main.rs | 12 +- crates/support/host/Cargo.toml | 4 + crates/support/host/src/lib.rs | 167 +- crates/support/scripts/container/start.sh | 33 +- crates/support/scripts/upload_program.sh | 79 + examples/CRISP/enclave.config.yaml | 33 +- templates/default/enclave.config.yaml | 12 +- 13 files changed, 2106 insertions(+), 168 deletions(-) create mode 100755 crates/support/scripts/upload_program.sh diff --git a/crates/cli/src/program.rs b/crates/cli/src/program.rs index 65b5e2a648..c93a9d87ae 100644 --- a/crates/cli/src/program.rs +++ b/crates/cli/src/program.rs @@ -28,6 +28,9 @@ pub enum ProgramCommands { /// Get a shell into the docker environment that the program runs in Shell, + /// Upload the compiled program to Pinata IPFS + Upload, + /// Commands to manage the program compilation cache Cache { #[command(subcommand)] @@ -50,6 +53,7 @@ pub async fn execute(command: ProgramCommands, config: &AppConfig) -> Result<()> e3_support_scripts::program_compile(config.program().clone(), dev).await? } ProgramCommands::Shell => e3_support_scripts::program_shell().await?, + ProgramCommands::Upload => e3_support_scripts::program_upload().await?, ProgramCommands::Cache { command } => match command { ProgramCacheCommands::Purge => e3_support_scripts::program_cache_purge().await?, }, diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index a12e66c6a1..1298e15da9 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -91,21 +91,41 @@ impl Default for NodeDefinition { } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub struct Risc0Config { +pub struct BoundlessConfig { + /// RPC URL for blockchain (e.g., Sepolia) + pub rpc_url: String, + /// Private key for submitting requests + pub private_key: String, + /// Pinata JWT for uploading programs/inputs #[serde(default)] - pub bonsai_api_key: Option, + pub pinata_jwt: Option, + /// Pre-uploaded program URL (if program is already on IPFS) #[serde(default)] - pub bonsai_api_url: Option, + pub program_url: Option, + /// Submit requests onchain (true) or offchain (false) + #[serde(default = "default_true")] + pub onchain: bool, +} + +fn default_true() -> bool { + true +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct Risc0Config { + /// Dev mode: 0 = production, 1 = dev mode (fake proofs) #[serde(default)] pub risc0_dev_mode: u8, + /// Boundless configuration + #[serde(default)] + pub boundless: Option, } impl Default for Risc0Config { fn default() -> Self { Risc0Config { - bonsai_api_key: None, - bonsai_api_url: None, - risc0_dev_mode: 0, + risc0_dev_mode: 1, // Default to dev mode for safety + boundless: None, } } } @@ -491,8 +511,6 @@ node: program: risc0: - bonsai_api_key: "12345678" - bonsai_api_url: "http://my.api.com" risc0_dev_mode: 0 nodes: @@ -530,9 +548,8 @@ nodes: assert_eq!( config.program().risc0(), Some(&Risc0Config { - bonsai_api_key: Some("12345678".to_string()), - bonsai_api_url: Some("http://my.api.com".to_string()), risc0_dev_mode: 0, + boundless: None, }) ); assert!(config.peers().is_empty()); diff --git a/crates/support-scripts/src/lib.rs b/crates/support-scripts/src/lib.rs index 129ab48d87..2a7e01b1b1 100644 --- a/crates/support-scripts/src/lib.rs +++ b/crates/support-scripts/src/lib.rs @@ -35,6 +35,15 @@ pub async fn program_shell() -> Result<()> { Ok(()) } +/// Upload the compiled program to Pinata IPFS +pub async fn program_upload() -> Result<()> { + let cwd = env::current_dir()?; + let script = cwd.join("crates/support/scripts/upload_program.sh"); + ensure_script_exists(&script).await?; + run_bash_script(&cwd, &script, &[]).await?; + Ok(()) +} + /// Purge all build caches from support pub async fn program_cache_purge() -> Result<()> { let cwd = env::current_dir()?; diff --git a/crates/support-scripts/src/program_risc0.rs b/crates/support-scripts/src/program_risc0.rs index db10caff10..00ae115c38 100644 --- a/crates/support-scripts/src/program_risc0.rs +++ b/crates/support-scripts/src/program_risc0.rs @@ -33,14 +33,25 @@ impl ProgramSupportApi for ProgramSupportRisc0 { let Some(risc0_config) = self.0.risc0() else { bail!("start must be run with risc0 config available"); }; - let risc0_dev_mode_str = risc0_config.risc0_dev_mode.to_string(); + let risc0_dev_mode_str = risc0_config.risc0_dev_mode.to_string(); let mut args = vec!["--risc0-dev-mode", risc0_dev_mode_str.as_str()]; - if let (Some(api_key), Some(api_url)) = - (&risc0_config.bonsai_api_key, &risc0_config.bonsai_api_url) - { - args.extend(["--api-key", api_key.as_str(), "--api-url", api_url.as_str()]); + // Boundless support + if let Some(boundless) = &risc0_config.boundless { + args.extend(["--rpc-url", boundless.rpc_url.as_str()]); + args.extend(["--private-key", boundless.private_key.as_str()]); + + if let Some(jwt) = &boundless.pinata_jwt { + args.extend(["--pinata-jwt", jwt.as_str()]); + } + + if let Some(url) = &boundless.program_url { + args.extend(["--program-url", url.as_str()]); + } + + let onchain = if boundless.onchain { "true" } else { "false" }; + args.extend(["--boundless-onchain", onchain]); } run_bash_script(&cwd, &script, &args).await?; diff --git a/crates/support/Cargo.lock b/crates/support/Cargo.lock index 30bd9f2fb3..cd81f36ebe 100644 --- a/crates/support/Cargo.lock +++ b/crates/support/Cargo.lock @@ -29,7 +29,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "base64", + "base64 0.22.1", "bitflags 2.9.1", "brotli", "bytes", @@ -253,8 +253,13 @@ dependencies = [ "alloy-core", "alloy-eips", "alloy-network", + "alloy-node-bindings", "alloy-provider", "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", "alloy-transport", "alloy-transport-http", ] @@ -267,6 +272,7 @@ checksum = "4195a29a4b87137b2bb02105e746102873bc03561805cf45c0e510c961f160e6" dependencies = [ "alloy-primitives", "num_enum", + "serde", "strum", ] @@ -415,6 +421,32 @@ dependencies = [ "sha2", ] +[[package]] +name = "alloy-genesis" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51b4c13e02a8104170a4de02ccf006d7c233e6c10ab290ee16e7041e6ac221d" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "serde", +] + +[[package]] +name = "alloy-hardforks" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165210652f71dfc094b051602bafd691f506c54050a174b1cba18fb5ef706a3" +dependencies = [ + "alloy-chains", + "alloy-eip2124", + "alloy-primitives", + "auto_impl", + "dyn-clone", +] + [[package]] name = "alloy-json-abi" version = "1.3.0" @@ -481,6 +513,27 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-node-bindings" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de75f0d0af3c6cb0bd3648e530289b2c542b7bf57e7d4296d1c29281418a476" +dependencies = [ + "alloy-genesis", + "alloy-hardforks", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "alloy-signer-local", + "k256", + "rand 0.8.5", + "serde_json", + "tempfile", + "thiserror 2.0.12", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" version = "1.3.0" @@ -520,12 +573,15 @@ dependencies = [ "alloy-json-rpc", "alloy-network", "alloy-network-primitives", + "alloy-node-bindings", "alloy-primitives", "alloy-rpc-client", + "alloy-rpc-types-anvil", "alloy-rpc-types-eth", "alloy-signer", "alloy-sol-types", "alloy-transport", + "alloy-transport-http", "async-stream", "async-trait", "auto_impl", @@ -534,14 +590,16 @@ dependencies = [ "futures", "futures-utils-wasm", "http 1.3.1", - "lru", + "lru 0.13.0", "parking_lot", "pin-project", + "reqwest", "serde", "serde_json", "thiserror 2.0.12", "tokio", "tracing", + "url", "wasmtimer", ] @@ -576,17 +634,44 @@ dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-transport", + "alloy-transport-http", "futures", "pin-project", + "reqwest", "serde", "serde_json", "tokio", "tokio-stream", "tower", "tracing", + "url", "wasmtimer", ] +[[package]] +name = "alloy-rpc-types" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d47b637369245d2dafef84b223b1ff5ea59e6cd3a98d2d3516e32788a0b216df" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b1f499acb3fc729615147bc113b8b798b17379f19d43058a687edc5792c102" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-rpc-types-any" version = "1.0.23" @@ -640,11 +725,27 @@ dependencies = [ "async-trait", "auto_impl", "either", - "elliptic-curve", + "elliptic-curve 0.13.8", "k256", "thiserror 2.0.12", ] +[[package]] +name = "alloy-signer-local" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7094c39cd41b03ed642145b0bd37251e31a9cf2ed19e1ce761f089867356a6" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand 0.8.5", + "thiserror 2.0.12", +] + [[package]] name = "alloy-sol-macro" version = "1.3.0" @@ -726,7 +827,7 @@ checksum = "f89bec2f59a41c0e259b6fe92f78dfc49862c17d10f938db9c33150d5a7f42b6" dependencies = [ "alloy-json-rpc", "alloy-primitives", - "base64", + "base64 0.22.1", "derive_more", "futures", "futures-utils-wasm", @@ -747,7 +848,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d3615ec64d775fec840f4e9d5c8e1f739eb1854d8d28db093fb3d4805e0cb53" dependencies = [ + "alloy-json-rpc", "alloy-transport", + "reqwest", + "serde_json", + "tower", + "tracing", "url", ] @@ -1257,6 +1363,189 @@ dependencies = [ "serde", ] +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-object-pool" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333c456b97c3f2d50604e8b2624253b7f787208cb72eb75e64b0ad11b221652c" +dependencies = [ + "async-std", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel 2.5.0", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.1", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-std" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -1279,6 +1568,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.88" @@ -1313,6 +1608,329 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-credential-types" +version = "1.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86590e57ea40121d47d3f2e131bfd873dea15d78dc2f4604f4734537ad9e56c4" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-lc-rs" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5932a7d9d28b0d2ea34c6b3779d35e3dd6f6345317c34e73438c4f1f29144151" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1826f2e4cfc2cd19ee53c42fbf68e2f81ec21108e0b7ecf6a71cf062137360fc" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "aws-runtime" +version = "1.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c034a1bc1d70e16e7f4e4caf7e9f7693e4c9c24cd91cf17c2a0b21abaebc7c8b" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.103.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af040a86ae4378b7ed2f62c83b36be1848709bbbf5757ec850d0e08596a26be9" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "lru 0.12.5", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sigv4" +version = "1.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35452ec3f001e1f2f6db107b6373f1f48f05ec63ba2c5c9fa91f07dad32af11" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.3.1", + "p256", + "percent-encoding", + "ring", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127fcfad33b7dfc531141fda7e1c402ac65f88aca5511a4d31e2e3d2cd01ce9c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.63.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9a26b2831e728924ec0089e92697a78a2f9cdcf90d81e8cfcc6a6c85080369" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc-fast", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e29a304f8319781a39808847efb39561351b1bb76e933da7aa90232673638658" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.62.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445d5d720c99eed0b4aa674ed00d835d9b1427dd73e04adaf2f94c6b2d6f9fca" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f108f1ca850f3feef3009bdcc977be201bca9a91058864d9de0684e64514bee0" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.27", + "h2 0.4.11", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.7", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.29", + "rustls-native-certs 0.8.2", + "rustls-pki-types", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.61.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db31f727935fc63c6eeae8b37b438847639ec330a9161ece694efba257e0c54" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1881b1ea6d313f9890710d65c158bdab6fb08c91ea825f74c1c8c357baf4cc" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e107ce0783019dbff59b3a244aa0c114e4a8c9d93498af9162608cd5474e796" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7204f9fd94749a7c53b26da1b961b4ac36bf070ef1e0b94bb09f79d4f6c193" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.3.1", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f535879a207fce0db74b679cfc3e91a3159c8144d717d55f5832aea9eef46e" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.3.1", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab77cdd036b11056d2a30a7af7b775789fb024bf216acc13884c6c97752ae56" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d79fb68e3d7fe5d4833ea34dc87d2e97d26d3086cb3da660bb6b1f76d98680b6" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version 0.4.1", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -1328,23 +1946,56 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "basic-cookies" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" +dependencies = [ + "lalrpop", + "lalrpop-util", + "regex", +] [[package]] name = "bincode" @@ -1355,15 +2006,50 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.104", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit-vec" version = "0.8.0" @@ -1434,6 +2120,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "blst" version = "0.3.15" @@ -1482,6 +2181,50 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "boundless-market" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b60977dc05d0a5b092d8fb30c2fe12e7408928ebf83a45117cc240f9e1375d4" +dependencies = [ + "alloy", + "alloy-chains", + "alloy-primitives", + "alloy-sol-types", + "anyhow", + "async-stream", + "async-trait", + "aws-sdk-s3", + "bytemuck", + "chrono", + "clap", + "dashmap", + "derive_builder", + "futures", + "futures-util", + "hex", + "httpmock", + "rand 0.9.2", + "reqwest", + "risc0-aggregation", + "risc0-ethereum-contracts 3.0.1", + "risc0-zkvm", + "rmp-serde", + "serde", + "serde_json", + "sha2", + "siwe", + "tempfile", + "thiserror 2.0.12", + "time", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-subscriber 0.3.19", + "url", + "utoipa", +] + [[package]] name = "brotli" version = "8.0.1" @@ -1550,6 +2293,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bytestring" version = "1.4.0" @@ -1617,6 +2370,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.1" @@ -1637,9 +2399,71 @@ checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", - "windows-link", + "wasm-bindgen", + "windows-link 0.1.3", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", ] [[package]] @@ -1657,6 +2481,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-hex" version = "1.14.1" @@ -1717,6 +2550,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1730,7 +2573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] @@ -1758,6 +2601,19 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc-fast" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf62af4cc77d8fe1c22dde4e721d87f2f54056139d8c412e1366b740305f56f" +dependencies = [ + "crc", + "digest 0.10.7", + "libc", + "rand 0.9.2", + "regex", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -1798,6 +2654,18 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -1869,6 +2737,22 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der" version = "0.7.10" @@ -1983,6 +2867,16 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.5.0" @@ -1991,10 +2885,21 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.2", "windows-sys 0.60.2", ] +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -2018,6 +2923,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf673e0848ef09fa4aeeba78e681cf651c0c7d35f76ee38cec8e55bc32fa111" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "downcast-rs" version = "1.2.1" @@ -2099,10 +3010,13 @@ name = "e3-support-host" version = "0.1.0" dependencies = [ "alloy-primitives", + "alloy-signer-local", "alloy-sol-types", "anyhow", "bincode", + "boundless-market", "bytemuck", + "dotenvy", "e3-compute-provider", "e3-user-program", "fhe", @@ -2111,11 +3025,12 @@ dependencies = [ "log", "methods", "rand 0.8.5", - "risc0-ethereum-contracts", + "risc0-ethereum-contracts 3.0.0", "risc0-zkvm", "serde", "tokio", "tracing-subscriber 0.3.19", + "url", ] [[package]] @@ -2138,19 +3053,31 @@ dependencies = [ "fhe-traits", ] +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", + "der 0.7.10", "digest 0.10.7", - "elliptic-curve", - "rfc6979", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", "serdect", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", ] [[package]] @@ -2180,21 +3107,41 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.5", "digest 0.10.7", - "ff", + "ff 0.13.1", "generic-array", - "group", - "pkcs8", + "group 0.13.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "serdect", "subtle", "zeroize", @@ -2212,6 +3159,15 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.35" @@ -2286,6 +3242,33 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2314,6 +3297,16 @@ dependencies = [ "bytes", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.1" @@ -2484,6 +3477,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -2538,6 +3537,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -2635,13 +3647,36 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.1", "rand_core 0.6.4", "subtle", ] @@ -2793,6 +3828,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -2812,7 +3858,7 @@ dependencies = [ "bytes", "futures-core", "http 1.3.1", - "http-body", + "http-body 1.0.1", "pin-project-lite", ] @@ -2828,6 +3874,58 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "httpmock" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ec9586ee0910472dec1a1f0f8acf52f0fdde93aea74d70d4a3107b4be0fd5b" +dependencies = [ + "assert-json-diff", + "async-object-pool", + "async-std", + "async-trait", + "base64 0.21.7", + "basic-cookies", + "crossbeam-utils", + "form_urlencoded", + "futures-util", + "hyper 0.14.32", + "lazy_static", + "levenshtein", + "log", + "regex", + "serde", + "serde_json", + "serde_regex", + "similar", + "tokio", + "url", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.6.0" @@ -2839,7 +3937,7 @@ dependencies = [ "futures-util", "h2 0.4.11", "http 1.3.1", - "http-body", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -2848,6 +3946,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.7" @@ -2855,12 +3969,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper", + "hyper 1.6.0", "hyper-util", - "rustls", + "rustls 0.23.29", + "rustls-native-certs 0.8.2", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.2", "tower-service", "webpki-roots", ] @@ -2873,7 +3988,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.6.0", "hyper-util", "native-tls", "tokio", @@ -2887,14 +4002,14 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", "http 1.3.1", - "http-body", - "hyper", + "http-body 1.0.1", + "hyper 1.6.0", "ipnet", "libc", "percent-encoding", @@ -3140,6 +4255,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -3224,8 +4348,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "serdect", "sha2", @@ -3250,6 +4374,46 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set 0.5.3", + "ena", + "itertools 0.11.0", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax 0.8.5", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata 0.4.9", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -3297,12 +4461,28 @@ dependencies = [ "hashbrown 0.15.4", ] +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + [[package]] name = "libc" version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + [[package]] name = "libm" version = "0.2.15" @@ -3375,6 +4555,18 @@ name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.4", +] [[package]] name = "lru" @@ -3441,6 +4633,16 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.7.5" @@ -3493,6 +4695,22 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3532,7 +4750,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3550,12 +4768,28 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "no_std_strings" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3790,12 +5024,29 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2", +] + [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -3824,6 +5075,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.4" @@ -3889,6 +5146,21 @@ dependencies = [ "indexmap 2.10.0", ] +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.10" @@ -3921,15 +5193,36 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.10", + "pkcs8 0.10.2", + "spki 0.7.3", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", ] [[package]] @@ -3938,8 +5231,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.10", + "spki 0.7.3", ] [[package]] @@ -3948,6 +5241,20 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -3999,6 +5306,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettyplease" version = "0.2.36" @@ -4090,8 +5403,8 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ - "bit-set", - "bit-vec", + "bit-set 0.8.0", + "bit-vec 0.8.0", "bitflags 2.9.1", "lazy_static", "num-traits", @@ -4198,7 +5511,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls", + "rustls 0.23.29", "socket2 0.5.10", "thiserror 2.0.12", "tokio", @@ -4218,7 +5531,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash", - "rustls", + "rustls 0.23.29", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -4378,6 +5691,17 @@ dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -4465,7 +5789,7 @@ version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", @@ -4473,20 +5797,21 @@ dependencies = [ "futures-util", "h2 0.4.11", "http 1.3.1", - "http-body", + "http-body 1.0.1", "http-body-util", - "hyper", - "hyper-rustls", + "hyper 1.6.0", + "hyper-rustls 0.27.7", "hyper-tls", "hyper-util", "js-sys", "log", "mime", + "mime_guess", "native-tls", "percent-encoding", "pin-project-lite", "quinn", - "rustls", + "rustls 0.23.29", "rustls-pki-types", "serde", "serde_json", @@ -4494,7 +5819,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.26.2", "tokio-util", "tower", "tower-http", @@ -4507,6 +5832,17 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -4531,6 +5867,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "risc0-aggregation" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b37f9050c74d66eb953591e88a60f2d347e99b121e7330eabb0f29c4053d2a36" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "bytemuck", + "hex", + "risc0-binfmt", + "risc0-zkp", + "risc0-zkvm", + "serde", + "thiserror 2.0.12", +] + [[package]] name = "risc0-binfmt" version = "3.0.2" @@ -4624,7 +5977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1b0689f4a270a2f247b04397ebb431b8f64fe5170e98ee4f9d71bd04825205" dependencies = [ "anyhow", - "bit-vec", + "bit-vec 0.8.0", "bytemuck", "derive_more", "paste", @@ -4647,14 +6000,33 @@ dependencies = [ [[package]] name = "risc0-ethereum-contracts" -version = "3.0.0" -source = "git+https://github.com/risc0/risc0-ethereum?tag=v3.0.0#32aa0b6f23ddd02dd93fc71717667606e5c7db86" +version = "3.0.0" +source = "git+https://github.com/risc0/risc0-ethereum?tag=v3.0.0#32aa0b6f23ddd02dd93fc71717667606e5c7db86" +dependencies = [ + "alloy", + "alloy-sol-types", + "anyhow", + "cfg-if", + "risc0-zkvm", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "risc0-ethereum-contracts" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a604f09f459f456fd9ef4919c6efcaa6a787a6f9ffcd76cfc81eae1860584a1" dependencies = [ "alloy", + "alloy-primitives", "alloy-sol-types", "anyhow", "cfg-if", + "hex", + "risc0-aggregation", "risc0-zkvm", + "serde", "thiserror 2.0.12", "tracing", ] @@ -4778,6 +6150,28 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -4800,10 +6194,10 @@ dependencies = [ "num-integer", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.10.2", "rand_core 0.6.4", - "signature", - "spki", + "signature 2.2.0", + "spki 0.7.3", "subtle", "zeroize", ] @@ -4891,20 +6285,66 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ + "aws-lc-rs", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.4", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.3.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -4915,12 +6355,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -4969,6 +6420,15 @@ dependencies = [ "yaml-rust2", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.27" @@ -5008,16 +6468,40 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", + "base16ct 0.2.0", + "der 0.7.10", "generic-array", - "pkcs8", + "pkcs8 0.10.2", "serdect", "subtle", "zeroize", @@ -5051,7 +6535,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.9.1", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -5126,6 +6623,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -5153,7 +6660,7 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -5185,7 +6692,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" dependencies = [ - "base16ct", + "base16ct 0.2.0", "serde", ] @@ -5255,6 +6762,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "2.2.0" @@ -5265,6 +6782,35 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "siwe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95bdefc0eedf06440b27092fbfe33f2cb493ad6a3423aa12cfe7f2aac44bd618" +dependencies = [ + "hex", + "http 1.3.1", + "iri-string", + "k256", + "rand 0.8.5", + "serde", + "sha3", + "thiserror 1.0.69", + "time", +] + [[package]] name = "slab" version = "0.4.10" @@ -5306,6 +6852,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -5313,7 +6869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", ] [[package]] @@ -5338,6 +6894,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5432,7 +7000,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.1", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5465,6 +7033,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -5629,13 +7208,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls", + "rustls 0.23.29", "tokio", ] @@ -5651,6 +7240,20 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "native-tls", + "tokio", + "tokio-native-tls", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -5730,7 +7333,7 @@ dependencies = [ "bytes", "futures-util", "http 1.3.1", - "http-body", + "http-body 1.0.1", "iri-string", "pin-project-lite", "tower", @@ -5827,6 +7430,25 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.3.1", + "httparse", + "log", + "native-tls", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + [[package]] name = "typenum" version = "1.18.0" @@ -5857,6 +7479,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -5886,6 +7514,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -5898,12 +7532,51 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + [[package]] name = "vcpkg" version = "0.2.15" @@ -5916,6 +7589,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "wait-timeout" version = "0.2.1" @@ -5925,6 +7604,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -6092,6 +7781,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.60.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -6106,7 +7804,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -6139,13 +7837,19 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-registry" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -6156,7 +7860,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6165,7 +7869,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6195,6 +7899,15 @@ dependencies = [ "windows-targets 0.53.2", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -6356,6 +8069,12 @@ dependencies = [ "tap", ] +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yaml-rust2" version = "0.10.4" diff --git a/crates/support/Cargo.toml b/crates/support/Cargo.toml index cc57a0bc21..58e022f633 100644 --- a/crates/support/Cargo.toml +++ b/crates/support/Cargo.toml @@ -20,6 +20,7 @@ alloy-primitives = { version = "=1.3.0", default-features = false, features = [ "std", ]} alloy-sol-types = { version = "=1.3.0" } +alloy-signer-local = { version = "=1.0.23" } anyhow = { version = "=1.0.98" } actix-web = "=4.11.0" bincode = { version = "=1.3.3" } @@ -42,6 +43,9 @@ e3-compute-provider = { git = "https://github.com/gnosisguild/enclave", rev = "2 tokio = { version = "=1.46.1", features = ["full"] } rand = { version = "=0.8.5" } tracing-subscriber = { version = "=0.3.19", features = ["env-filter"] } +boundless-market = { version = "=1.1.0" } +url = { version = "=2.5.4" } +dotenvy = { version = "=0.15.7" } [profile.release] debug = 1 diff --git a/crates/support/app/src/main.rs b/crates/support/app/src/main.rs index 626b429e25..71d33f976a 100644 --- a/crates/support/app/src/main.rs +++ b/crates/support/app/src/main.rs @@ -38,23 +38,15 @@ async fn call_webhook( Ok(()) } -#[cfg(feature = "risc0")] async fn run_computation_async(fhe_inputs: FHEInputs) -> anyhow::Result<(Vec, Vec)> { println!("running computation..."); - let (risc0_output, ciphertext) = + let (boundless_output, ciphertext) = tokio::task::spawn_blocking(move || e3_support_host::run_compute(fhe_inputs)).await??; println!("have result from computation!"); - let proof: Vec = risc0_output.seal.into(); + let proof: Vec = boundless_output.seal.into(); Ok((proof, ciphertext)) } -#[cfg(not(feature = "risc0"))] -async fn run_computation_async(fhe_inputs: FHEInputs) -> anyhow::Result<(Vec, Vec)> { - println!("NOOP: risc0 feature not enabled, skipping actual computation"); - // Return dummy data - Ok((vec![0u8; 32], vec![1u8; 64])) -} - async fn handle_webhook_delivery( e3_id: u64, callback_url: &str, diff --git a/crates/support/host/Cargo.toml b/crates/support/host/Cargo.toml index 350169a77b..7f309633f0 100644 --- a/crates/support/host/Cargo.toml +++ b/crates/support/host/Cargo.toml @@ -9,6 +9,7 @@ bytemuck = { workspace = true } serde = { workspace = true } alloy-primitives = { workspace = true } alloy-sol-types = { workspace = true } +alloy-signer-local = { workspace = true } anyhow = { workspace = true } log = { workspace = true } methods = { workspace = true } @@ -22,3 +23,6 @@ fhe-util = { workspace = true } e3-user-program = { workspace = true } rand = { workspace = true } tracing-subscriber = { workspace = true } +boundless-market = { workspace = true } +url = { workspace = true } +dotenvy = { workspace = true } diff --git a/crates/support/host/src/lib.rs b/crates/support/host/src/lib.rs index b63c2e0dfd..5b6b383a7e 100644 --- a/crates/support/host/src/lib.rs +++ b/crates/support/host/src/lib.rs @@ -4,80 +4,145 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use anyhow::{Error, Result}; +use anyhow::{Context, Result}; use bincode::serialize; use e3_compute_provider::{ ComputeInput, ComputeManager, ComputeProvider, ComputeResult, FHEInputs, }; +use alloy_signer_local::PrivateKeySigner; use e3_user_program::fhe_processor; use methods::PROGRAM_ELF; -use risc0_ethereum_contracts::groth16; -use risc0_zkvm::{default_prover, ExecutorEnv, ProverOpts, VerifierContext}; +use boundless_market::{Client, storage::storage_provider_from_env, contracts::FulfillmentData}; +use url::Url; use serde::{Deserialize, Serialize}; -use std::time::Instant; +use std::time::{Duration, Instant}; -fn encode_input(input: &[u8]) -> Result, Error> { - Ok(bytemuck::pod_collect_to_vec(&risc0_zkvm::serde::to_vec( - input, - )?)) -} - -pub struct Risc0Provider; +pub struct BoundlessProvider; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Risc0Output { +pub struct BoundlessOutput { pub result: ComputeResult, pub bytes: Vec, pub seal: Vec, } -impl ComputeProvider for Risc0Provider { - type Output = Risc0Output; +impl ComputeProvider for BoundlessProvider { + type Output = BoundlessOutput; fn prove(&self, input: &ComputeInput) -> Self::Output { - let encoded_input = encode_input(&serialize(input).unwrap()).unwrap(); - let env = ExecutorEnv::builder() - .write_slice(&encoded_input) - .build() - .unwrap(); - - let receipt = default_prover() - .prove_with_ctx( - env, - &VerifierContext::default(), - PROGRAM_ELF, - &ProverOpts::groth16(), - ) - .unwrap() - .receipt; - - let decoded_journal = receipt.journal.decode().unwrap(); - - // Check if RISC0_DEV_MODE is set to "1" (dev mode) - // If dev mode: return empty seal (fake proof) - // Otherwise: return real groth16 proof - let is_dev_mode = std::env::var("RISC0_DEV_MODE").unwrap_or_default() == "1"; - - let seal = if is_dev_mode { - println!("RISC0_DEV_MODE=1: Using fake proof (empty seal)"); - vec![] + let is_dev_mode = std::env::var("RISC0_DEV_MODE") + .unwrap_or_else(|_| "0".to_string()) == "1"; + + if is_dev_mode { + println!("Dev mode: Using fake proof"); + fake_prove(input) } else { - println!("RISC0_DEV_MODE=0 or unset: Generating real Groth16 proof"); - groth16::encode(receipt.inner.groth16().unwrap().seal.clone()).unwrap() - }; - - Risc0Output { - result: decoded_journal, - bytes: receipt.journal.bytes.clone(), - seal, + println!("Using Boundless for proving"); + tokio::runtime::Handle::current() + .block_on(boundless_prove(input)) + .expect("Boundless proving failed") } } } -pub fn run_compute(params: FHEInputs) -> Result<(Risc0Output, Vec)> { - let risc0_provider = Risc0Provider; +/// Dev mode: return fake proof without executing +fn fake_prove(input: &ComputeInput) -> BoundlessOutput { + println!("Generating fake proof for dev mode"); + + // Execute the program with the input + let result = input.process(fhe_processor); + + // Serialize the result as journal bytes + let journal_bytes = bincode::serialize(&result).unwrap_or_default(); + + BoundlessOutput { + result, + bytes: journal_bytes, + seal: vec![], // No seal in dev mode + } +} + +/// Boundless proving +async fn boundless_prove(input: &ComputeInput) -> Result { + println!("Submitting proof request to Boundless..."); + + let rpc_url = std::env::var("RPC_URL") + .context("RPC_URL not set")? + .parse() + .context("Invalid RPC_URL")?; + + let private_key: PrivateKeySigner = std::env::var("PRIVATE_KEY") + .context("PRIVATE_KEY not set")? + .parse() + .context("Invalid PRIVATE_KEY")?; + + let client = Client::builder() + .with_rpc_url(rpc_url) + .with_private_key(private_key) + .with_storage_provider(Some(storage_provider_from_env()?)) + .build() + .await + .context("Failed to build Boundless client")?; + + let input_bytes = serialize(input)?; + let program_url = std::env::var("PROGRAM_URL").ok(); + + let request = if let Some(url) = program_url { + println!("Using pre-uploaded program: {}", url); + client.new_request() + .with_program_url(url.parse::().context("Failed to parse program URL")?) + .context("Failed to create new request")? + .with_stdin(input_bytes) + } else { + println!("Warning: Uploading {}MB program at runtime", PROGRAM_ELF.len() / 1_000_000); + client.new_request() + .with_program(PROGRAM_ELF) + .with_stdin(input_bytes) + }; + + let onchain = std::env::var("BOUNDLESS_ONCHAIN") + .unwrap_or_else(|_| "true".to_string()) == "true"; + + let (request_id, expires_at) = if onchain { + println!("Submitting onchain..."); + client.submit_onchain(request).await? + } else { + println!("Submitting offchain..."); + client.submit_offchain(request).await? + }; + + println!("Request ID: {:x}, waiting for fulfillment...", request_id); + + let fulfillment = client + .wait_for_request_fulfillment( + request_id, + Duration::from_secs(5), + expires_at, + ) + .await + .context("Failed to wait for fulfillment")?; + + println!("Proof received from Boundless!"); + let data = fulfillment.data(); + let (_, journal) = match data { + Ok(FulfillmentData::ImageIdAndJournal(image_id, journal)) => (image_id, journal), + _ => return Err(anyhow::anyhow!("Invalid fulfillment data")), + }; + + let decoded_journal: ComputeResult = bincode::deserialize(&journal) + .context("Failed to decode journal")?; + + Ok(BoundlessOutput { + result: decoded_journal, + bytes: journal.to_vec(), + seal: fulfillment.seal.to_vec(), + }) +} + +pub fn run_compute(params: FHEInputs) -> Result<(BoundlessOutput, Vec)> { + let boundless_provider = BoundlessProvider; - let mut provider = ComputeManager::new(risc0_provider, params, fhe_processor, false, None); + let mut provider = ComputeManager::new(boundless_provider, params, fhe_processor, false, None); // Start timer let start_time = Instant::now(); diff --git a/crates/support/scripts/container/start.sh b/crates/support/scripts/container/start.sh index c282ecc390..d0218822ad 100755 --- a/crates/support/scripts/container/start.sh +++ b/crates/support/scripts/container/start.sh @@ -1,22 +1,34 @@ #!/usr/bin/env bash # Clear any existing environment variables -unset BONSAI_API_KEY BONSAI_API_URL +unset RISC0_DEV_MODE RPC_URL PRIVATE_KEY PINATA_JWT PROGRAM_URL BOUNDLESS_ONCHAIN # Parse command line arguments POSITIONAL=() while [[ $# -gt 0 ]]; do case $1 in - --api-key) - export BONSAI_API_KEY="$2" + --risc0-dev-mode) + export RISC0_DEV_MODE="$2" shift 2 ;; - --api-url) - export BONSAI_API_URL="$2" + --rpc-url) + export RPC_URL="$2" shift 2 ;; - --risc0-dev-mode) - export RISC0_DEV_MODE="$2" + --private-key) + export PRIVATE_KEY="$2" + shift 2 + ;; + --pinata-jwt) + export PINATA_JWT="$2" + shift 2 + ;; + --program-url) + export PROGRAM_URL="$2" + shift 2 + ;; + --boundless-onchain) + export BOUNDLESS_ONCHAIN="$2" shift 2 ;; *) @@ -30,10 +42,15 @@ set -- "${POSITIONAL[@]}" CARGO_INCREMENTAL=1 +# Default to dev mode if no Boundless configuration provided if [ -z "$RISC0_DEV_MODE" ]; then - [ -z "$BONSAI_API_KEY" ] && export RISC0_DEV_MODE=1 + if [ -z "$RPC_URL" ]; then + export RISC0_DEV_MODE=1 + echo "No Boundless config found, defaulting to dev mode" + fi fi echo "RISC0_DEV_MODE=$RISC0_DEV_MODE" +[ -n "$RPC_URL" ] && echo "Using Boundless (RPC: $RPC_URL)" exec cargo run --bin e3-support-app "$@" diff --git a/crates/support/scripts/upload_program.sh b/crates/support/scripts/upload_program.sh new file mode 100755 index 0000000000..c50378e61d --- /dev/null +++ b/crates/support/scripts/upload_program.sh @@ -0,0 +1,79 @@ +#!/bin/bash +set -e + +# Upload PROGRAM_ELF to Pinata and cache the URL +# Run this when your program changes to avoid runtime uploads + +# Determine the script's directory and find the support crate +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SUPPORT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" + +PROGRAM_PATH="$SUPPORT_DIR/target/riscv-guest/methods/guests/riscv32im-risc0-zkvm-elf/release/program.bin" +HASH_FILE="./.program_hash" +URL_FILE="./.program_url" + +if [ ! -f "$PROGRAM_PATH" ]; then + echo "Error: Program not found at $PROGRAM_PATH" + echo "Run: enclave program compile" + exit 1 +fi + +if [ -z "$PINATA_JWT" ]; then + echo "Error: PINATA_JWT environment variable not set" + exit 1 +fi + +# Calculate hash +CURRENT_HASH=$(sha256sum "$PROGRAM_PATH" | awk '{print $1}') + +# Check if already uploaded +if [ -f "$HASH_FILE" ] && [ -f "$URL_FILE" ]; then + STORED_HASH=$(cat "$HASH_FILE") + if [ "$CURRENT_HASH" = "$STORED_HASH" ]; then + echo "Program unchanged. Existing URL:" + cat "$URL_FILE" + exit 0 + fi +fi + +echo "Uploading program to Pinata..." + +# Upload +RESPONSE=$(curl -s -X POST "https://api.pinata.cloud/pinning/pinFileToIPFS" \ + -H "Authorization: Bearer $PINATA_JWT" \ + -F "file=@$PROGRAM_PATH;filename=program.bin") + +# Extract CID +CID=$(echo "$RESPONSE" | grep -o '"IpfsHash":"[^"]*' | cut -d'"' -f4) + +if [ -z "$CID" ]; then + echo "Upload failed:" + echo "$RESPONSE" + exit 1 +fi + +# Save +PROGRAM_URL="https://gateway.pinata.cloud/ipfs/$CID" +echo "$CURRENT_HASH" > "$HASH_FILE" +echo "$PROGRAM_URL" > "$URL_FILE" + +echo "" +echo "✅ Upload successful!" +echo "" +echo "Program URL:" +echo "$PROGRAM_URL" +echo "" +echo "The URL has been saved to .program_url" +echo "" +echo "To use this in production, add to your enclave.config.yaml:" +echo "" +echo "program:" +echo " risc0:" +echo " risc0_dev_mode: 0" +echo " boundless:" +echo " rpc_url: \"https://sepolia.infura.io/v3/YOUR_KEY\"" +echo " private_key: \"\${PRIVATE_KEY}\"" +echo " pinata_jwt: \"\${PINATA_JWT}\"" +echo " program_url: \"$PROGRAM_URL\"" +echo " onchain: true" + diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index c79f839aea..f26f244869 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -3,50 +3,61 @@ chains: rpc_url: ws://localhost:8545 contracts: e3_program: - address: '0x67d269191c92Caf3cD7723F116c85e6E9bf55933' + address: "0xc5a5C42992dECbae36851359345FE25997F5C42d" deploy_block: 1 enclave: - address: '0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0' + address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" deploy_block: 13 ciphernode_registry: - address: '0x610178dA211FEF7D417bC0e6FeD39F05609AD788' + address: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" deploy_block: 11 bonding_registry: - address: '0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6' + address: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" deploy_block: 8 fee_token: - address: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0' + address: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" deploy_block: 4 program: dev: true + # risc0: + # risc0_dev_mode: 1 # 0 = production (Boundless), 1 = dev mode (fake proofs) + # + # # Boundless configuration (for production) + # boundless: + # rpc_url: "https://sepolia.infura.io/v3/YOUR_KEY" + # private_key: "${PRIVATE_KEY}" # Use env vars for secrets + # pinata_jwt: "${PINATA_JWT}" # For uploading programs + # program_url: "https://gateway.pinata.cloud/ipfs/YOUR_CID" # Pre-uploaded program + # onchain: true # true = onchain requests, false = offchain + nodes: cn1: - address: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' + address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" quic_port: 9201 autonetkey: true autopassword: true cn2: - address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' + address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" quic_port: 9202 autonetkey: true autopassword: true cn3: - address: '0x90F79bf6EB2c4f870365E785982E1f101E93b906' + address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906" quic_port: 9203 autonetkey: true autopassword: true cn4: - address: '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65' + address: "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" quic_port: 9204 autonetkey: true autopassword: true cn5: - address: '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc' + address: "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" quic_port: 9205 autonetkey: true autopassword: true ag: - address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" quic_port: 9206 autonetkey: true autopassword: true diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 5036ac2770..e98ab40896 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -21,9 +21,15 @@ chains: program: dev: true # risc0: - # risc0_dev_mode: 1 # 0 = real groth16 proofs, 1 = fake proofs (dev mode) - # bonsai_api_key: xxxxxxxxxxxxxxxx - # bonsai_api_url: xxxxxxxxxxxxxxxx + # risc0_dev_mode: 1 # 0 = production (Boundless), 1 = dev mode (fake proofs) + # + # # Boundless configuration (for production) + # boundless: + # rpc_url: "https://sepolia.infura.io/v3/YOUR_KEY" + # private_key: "${PRIVATE_KEY}" # Use env vars for secrets + # pinata_jwt: "${PINATA_JWT}" # For uploading programs + # program_url: "https://gateway.pinata.cloud/ipfs/YOUR_CID" # Pre-uploaded program + # onchain: true # true = onchain requests, false = offchain nodes: cn1: From 9e40517cea8bcba9fb0438e5afcbe7f83dbbf5ad Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Wed, 19 Nov 2025 17:18:51 +0500 Subject: [PATCH 02/10] feat: upload program to ipfs --- crates/cli/src/program.rs | 4 +- crates/support-scripts/ctl/start | 51 ++++++--- crates/support-scripts/ctl/upload | 24 ++++ crates/support-scripts/src/lib.rs | 14 +-- crates/support-scripts/src/program.rs | 7 ++ crates/support-scripts/src/program_dev.rs | 8 ++ crates/support-scripts/src/program_risc0.rs | 20 ++++ crates/support-scripts/src/traits.rs | 1 + crates/support/scripts/container/upload.sh | 116 ++++++++++++++++++++ crates/support/scripts/upload_program.sh | 79 ------------- examples/CRISP/enclave.config.yaml | 12 +- templates/default/enclave.config.yaml | 4 +- 12 files changed, 228 insertions(+), 112 deletions(-) create mode 100755 crates/support-scripts/ctl/upload create mode 100755 crates/support/scripts/container/upload.sh delete mode 100755 crates/support/scripts/upload_program.sh diff --git a/crates/cli/src/program.rs b/crates/cli/src/program.rs index c93a9d87ae..9f4578465a 100644 --- a/crates/cli/src/program.rs +++ b/crates/cli/src/program.rs @@ -53,7 +53,9 @@ pub async fn execute(command: ProgramCommands, config: &AppConfig) -> Result<()> e3_support_scripts::program_compile(config.program().clone(), dev).await? } ProgramCommands::Shell => e3_support_scripts::program_shell().await?, - ProgramCommands::Upload => e3_support_scripts::program_upload().await?, + ProgramCommands::Upload => { + e3_support_scripts::program_upload(config.program().clone(), None).await? + } ProgramCommands::Cache { command } => match command { ProgramCacheCommands::Purge => e3_support_scripts::program_cache_purge().await?, }, diff --git a/crates/support-scripts/ctl/start b/crates/support-scripts/ctl/start index 1e037ceba9..08eec573b6 100755 --- a/crates/support-scripts/ctl/start +++ b/crates/support-scripts/ctl/start @@ -1,21 +1,31 @@ #!/usr/bin/env bash -# Clear any existing environment variables -unset API_KEY API_URL RISC0_DEV_MODE +unset RISC0_DEV_MODE RPC_URL PRIVATE_KEY PINATA_JWT PROGRAM_URL BOUNDLESS_ONCHAIN -# Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in - --api-key) - API_KEY="$2" + --risc0-dev-mode) + RISC0_DEV_MODE="$2" shift 2 ;; - --api-url) - API_URL="$2" + --rpc-url) + RPC_URL="$2" shift 2 ;; - --risc0-dev-mode) - RISC0_DEV_MODE="$2" + --private-key) + PRIVATE_KEY="$2" + shift 2 + ;; + --pinata-jwt) + PINATA_JWT="$2" + shift 2 + ;; + --program-url) + PROGRAM_URL="$2" + shift 2 + ;; + --boundless-onchain) + BOUNDLESS_ONCHAIN="$2" shift 2 ;; *) @@ -32,11 +42,24 @@ if [[ -n "$RISC0_DEV_MODE" ]]; then CONTAINER_ARGS+=("--risc0-dev-mode" "$RISC0_DEV_MODE") fi -if [[ -n "$API_KEY" && -n "$API_URL" ]]; then - CONTAINER_ARGS+=("--api-key" "$API_KEY" "--api-url" "$API_URL") -elif [[ -n "$API_KEY" || -n "$API_URL" ]]; then - echo "Error: Both --api-key and --api-url must be provided together, or neither" +if [[ -n "$RPC_URL" && -n "$PRIVATE_KEY" ]]; then + CONTAINER_ARGS+=("--rpc-url" "$RPC_URL") + CONTAINER_ARGS+=("--private-key" "$PRIVATE_KEY") +elif [[ -n "$RPC_URL" || -n "$PRIVATE_KEY" ]]; then + echo "Error: Both --rpc-url and --private-key must be provided together, or neither" exit 1 fi -exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}" +if [[ -n "$PINATA_JWT" ]]; then + CONTAINER_ARGS+=("--pinata-jwt" "$PINATA_JWT") +fi + +if [[ -n "$PROGRAM_URL" ]]; then + CONTAINER_ARGS+=("--program-url" "$PROGRAM_URL") +fi + +if [[ -n "$BOUNDLESS_ONCHAIN" ]]; then + CONTAINER_ARGS+=("--boundless-onchain" "$BOUNDLESS_ONCHAIN") +fi + +exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}" \ No newline at end of file diff --git a/crates/support-scripts/ctl/upload b/crates/support-scripts/ctl/upload new file mode 100755 index 0000000000..5f83267346 --- /dev/null +++ b/crates/support-scripts/ctl/upload @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +PINATA_JWT="" +while [[ $# -gt 0 ]]; do + case $1 in + --pinata-jwt) + PINATA_JWT="$2" + shift 2 + ;; + *) + echo "Unknown argument: $1" + exit 1 + ;; + esac +done + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONTAINER_ARGS=("./scripts/container/upload.sh") + +if [[ -n "$PINATA_JWT" ]]; then + CONTAINER_ARGS+=("--pinata-jwt" "$PINATA_JWT") +fi + +exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}" \ No newline at end of file diff --git a/crates/support-scripts/src/lib.rs b/crates/support-scripts/src/lib.rs index 2a7e01b1b1..e28783c13c 100644 --- a/crates/support-scripts/src/lib.rs +++ b/crates/support-scripts/src/lib.rs @@ -26,6 +26,11 @@ pub async fn program_start(program_config: ProgramConfig, is_dev: Option) ProgramSupport::new(program_config, is_dev).start().await } +/// Upload the compiled program to Pinata IPFS +pub async fn program_upload(program_config: ProgramConfig, is_dev: Option) -> Result<()> { + ProgramSupport::new(program_config, is_dev).upload().await +} + /// Open up a shell in the docker container pub async fn program_shell() -> Result<()> { let cwd = env::current_dir()?; @@ -35,15 +40,6 @@ pub async fn program_shell() -> Result<()> { Ok(()) } -/// Upload the compiled program to Pinata IPFS -pub async fn program_upload() -> Result<()> { - let cwd = env::current_dir()?; - let script = cwd.join("crates/support/scripts/upload_program.sh"); - ensure_script_exists(&script).await?; - run_bash_script(&cwd, &script, &[]).await?; - Ok(()) -} - /// Purge all build caches from support pub async fn program_cache_purge() -> Result<()> { let cwd = env::current_dir()?; diff --git a/crates/support-scripts/src/program.rs b/crates/support-scripts/src/program.rs index 0baaae5b3f..52e486637b 100644 --- a/crates/support-scripts/src/program.rs +++ b/crates/support-scripts/src/program.rs @@ -48,4 +48,11 @@ impl ProgramSupportApi for ProgramSupport { ProgramSupport::Risc0(s) => s.start().await, } } + + async fn upload(&self) -> Result<()> { + match self { + ProgramSupport::Dev(s) => s.upload().await, + ProgramSupport::Risc0(s) => s.upload().await, + } + } } diff --git a/crates/support-scripts/src/program_dev.rs b/crates/support-scripts/src/program_dev.rs index 1d5800788b..fc534b151b 100644 --- a/crates/support-scripts/src/program_dev.rs +++ b/crates/support-scripts/src/program_dev.rs @@ -33,4 +33,12 @@ impl ProgramSupportApi for ProgramSupportDev { run_bash_script(&cwd, &script, &[]).await?; Ok(()) } + + async fn upload(&self) -> Result<()> { + let cwd = env::current_dir()?; + let script = cwd.join(".enclave/support/ctl/upload"); + ensure_script_exists(&script).await?; + run_bash_script(&cwd, &script, &[]).await?; + Ok(()) + } } diff --git a/crates/support-scripts/src/program_risc0.rs b/crates/support-scripts/src/program_risc0.rs index 00ae115c38..d238ba0945 100644 --- a/crates/support-scripts/src/program_risc0.rs +++ b/crates/support-scripts/src/program_risc0.rs @@ -57,4 +57,24 @@ impl ProgramSupportApi for ProgramSupportRisc0 { run_bash_script(&cwd, &script, &args).await?; Ok(()) } + + /// Upload the compiled program to Pinata IPFS + async fn upload(&self) -> Result<()> { + let cwd = env::current_dir()?; + let script = cwd.join(".enclave/support/ctl/upload"); + ensure_script_exists(&script).await?; + + let mut args = vec![]; + + if let Some(risc0_config) = self.0.risc0() { + if let Some(boundless) = &risc0_config.boundless { + if let Some(jwt) = &boundless.pinata_jwt { + args.extend(["--pinata-jwt", jwt.as_str()]); + } + } + } + + run_bash_script(&cwd, &script, &args).await?; + Ok(()) + } } diff --git a/crates/support-scripts/src/traits.rs b/crates/support-scripts/src/traits.rs index ab80dca5d9..2145e6a4d8 100644 --- a/crates/support-scripts/src/traits.rs +++ b/crates/support-scripts/src/traits.rs @@ -11,4 +11,5 @@ use async_trait::async_trait; pub trait ProgramSupportApi { async fn compile(&self) -> Result<()>; async fn start(&self) -> Result<()>; + async fn upload(&self) -> Result<()>; } diff --git a/crates/support/scripts/container/upload.sh b/crates/support/scripts/container/upload.sh new file mode 100755 index 0000000000..febedefc43 --- /dev/null +++ b/crates/support/scripts/container/upload.sh @@ -0,0 +1,116 @@ +#!/bin/bash +set -e + +while [[ $# -gt 0 ]]; do + case $1 in + --pinata-jwt) + export PINATA_JWT="$2" + shift 2 + ;; + *) + shift + ;; + esac +done + +PROGRAM_PATH="./target/riscv-guest/methods/guests/riscv32im-risc0-zkvm-elf/release/program.bin" + +HASH_FILE="./target/.program_hash" +URL_FILE="./target/.program_url" + +if [ ! -f "$PROGRAM_PATH" ]; then + echo "Error: Program not found at $PROGRAM_PATH" + echo "Run: enclave program compile" + exit 1 +fi + +if [ -z "$PINATA_JWT" ]; then + echo "Error: PINATA_JWT environment variable not set" + echo "" + echo "Please set your Pinata JWT token:" + echo " export PINATA_JWT=\"your_jwt_token\"" + echo "" + echo "Get your JWT from: https://pinata.cloud" + exit 1 +fi + +CURRENT_HASH=$(sha256sum "$PROGRAM_PATH" | awk '{print $1}') +HASH_PREFIX="${CURRENT_HASH:0:8}" +TIMESTAMP=$(date +%s) +FILE_SIZE=$(stat -f%z "$PROGRAM_PATH" 2>/dev/null || stat -c%s "$PROGRAM_PATH" 2>/dev/null || du -b "$PROGRAM_PATH" | awk '{print $1}') +FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024)) +FILE_SIZE_KB=$((FILE_SIZE / 1024)) +MODIFIED_TIME=$(stat -f%Sm "$PROGRAM_PATH" 2>/dev/null || stat -c%y "$PROGRAM_PATH" 2>/dev/null || date -r "$PROGRAM_PATH" 2>/dev/null || echo "unknown") + +FILENAME="program-${HASH_PREFIX}-${TIMESTAMP}.bin" + +echo "==========================================" +echo "Program Upload Metadata" +echo "==========================================" +echo "File path: $PROGRAM_PATH" +echo "File size: $FILE_SIZE bytes (${FILE_SIZE_MB} MB / ${FILE_SIZE_KB} KB)" +echo "SHA256 hash: $CURRENT_HASH" +echo "Modified: $MODIFIED_TIME" +echo "Upload name: $FILENAME" +echo "==========================================" +echo "" + +if [ -f "$HASH_FILE" ] && [ -f "$URL_FILE" ]; then + STORED_HASH=$(cat "$HASH_FILE") + if [ "$CURRENT_HASH" = "$STORED_HASH" ]; then + echo "Program unchanged (hash matches). Existing URL:" + cat "$URL_FILE" + exit 0 + else + echo "Program changed (hash differs). Uploading new version..." + echo " Old hash: $STORED_HASH" + echo " New hash: $CURRENT_HASH" + echo "" + fi +fi + +echo "Uploading program to Pinata as '$FILENAME'..." + +RESPONSE=$(curl -s -X POST "https://api.pinata.cloud/pinning/pinFileToIPFS" \ + -H "Authorization: Bearer $PINATA_JWT" \ + -F "file=@$PROGRAM_PATH;filename=$FILENAME") + +CID=$(echo "$RESPONSE" | grep -o '"IpfsHash":"[^"]*' | cut -d'"' -f4) + +if [ -z "$CID" ]; then + echo "Upload failed:" + echo "$RESPONSE" + exit 1 +fi + +# Save +PROGRAM_URL="https://gateway.pinata.cloud/ipfs/$CID" +echo "$CURRENT_HASH" > "$HASH_FILE" +echo "$PROGRAM_URL" > "$URL_FILE" + +echo "" +echo "✅ Upload successful!" +echo "" +echo "==========================================" +echo "Upload Confirmation" +echo "==========================================" +echo "IPFS CID: $CID" +echo "Program URL: $PROGRAM_URL" +echo "SHA256 hash: $CURRENT_HASH" +echo "File size: $FILE_SIZE bytes (${FILE_SIZE_MB} MB / ${FILE_SIZE_KB} KB)" +echo "Upload name: $FILENAME" +echo "==========================================" +echo "" +echo "The URL has been saved to .program_url" +echo "" +echo "To use this in production, add to your enclave.config.yaml:" +echo "" +echo "program:" +echo " risc0:" +echo " risc0_dev_mode: 0" +echo " boundless:" +echo " rpc_url: \"https://sepolia.infura.io/v3/YOUR_KEY\"" +echo " private_key: \"\${PRIVATE_KEY}\"" +echo " pinata_jwt: \"\${PINATA_JWT}\"" +echo " program_url: \"$PROGRAM_URL\"" +echo " onchain: true" \ No newline at end of file diff --git a/crates/support/scripts/upload_program.sh b/crates/support/scripts/upload_program.sh deleted file mode 100755 index c50378e61d..0000000000 --- a/crates/support/scripts/upload_program.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash -set -e - -# Upload PROGRAM_ELF to Pinata and cache the URL -# Run this when your program changes to avoid runtime uploads - -# Determine the script's directory and find the support crate -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SUPPORT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" - -PROGRAM_PATH="$SUPPORT_DIR/target/riscv-guest/methods/guests/riscv32im-risc0-zkvm-elf/release/program.bin" -HASH_FILE="./.program_hash" -URL_FILE="./.program_url" - -if [ ! -f "$PROGRAM_PATH" ]; then - echo "Error: Program not found at $PROGRAM_PATH" - echo "Run: enclave program compile" - exit 1 -fi - -if [ -z "$PINATA_JWT" ]; then - echo "Error: PINATA_JWT environment variable not set" - exit 1 -fi - -# Calculate hash -CURRENT_HASH=$(sha256sum "$PROGRAM_PATH" | awk '{print $1}') - -# Check if already uploaded -if [ -f "$HASH_FILE" ] && [ -f "$URL_FILE" ]; then - STORED_HASH=$(cat "$HASH_FILE") - if [ "$CURRENT_HASH" = "$STORED_HASH" ]; then - echo "Program unchanged. Existing URL:" - cat "$URL_FILE" - exit 0 - fi -fi - -echo "Uploading program to Pinata..." - -# Upload -RESPONSE=$(curl -s -X POST "https://api.pinata.cloud/pinning/pinFileToIPFS" \ - -H "Authorization: Bearer $PINATA_JWT" \ - -F "file=@$PROGRAM_PATH;filename=program.bin") - -# Extract CID -CID=$(echo "$RESPONSE" | grep -o '"IpfsHash":"[^"]*' | cut -d'"' -f4) - -if [ -z "$CID" ]; then - echo "Upload failed:" - echo "$RESPONSE" - exit 1 -fi - -# Save -PROGRAM_URL="https://gateway.pinata.cloud/ipfs/$CID" -echo "$CURRENT_HASH" > "$HASH_FILE" -echo "$PROGRAM_URL" > "$URL_FILE" - -echo "" -echo "✅ Upload successful!" -echo "" -echo "Program URL:" -echo "$PROGRAM_URL" -echo "" -echo "The URL has been saved to .program_url" -echo "" -echo "To use this in production, add to your enclave.config.yaml:" -echo "" -echo "program:" -echo " risc0:" -echo " risc0_dev_mode: 0" -echo " boundless:" -echo " rpc_url: \"https://sepolia.infura.io/v3/YOUR_KEY\"" -echo " private_key: \"\${PRIVATE_KEY}\"" -echo " pinata_jwt: \"\${PINATA_JWT}\"" -echo " program_url: \"$PROGRAM_URL\"" -echo " onchain: true" - diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index f26f244869..92bb4b9d16 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -20,15 +20,13 @@ chains: program: dev: true # risc0: - # risc0_dev_mode: 1 # 0 = production (Boundless), 1 = dev mode (fake proofs) - # - # # Boundless configuration (for production) + # risc0_dev_mode: 0 # 0 = production (Boundless), 1 = dev mode (fake proofs) # boundless: # rpc_url: "https://sepolia.infura.io/v3/YOUR_KEY" - # private_key: "${PRIVATE_KEY}" # Use env vars for secrets - # pinata_jwt: "${PINATA_JWT}" # For uploading programs - # program_url: "https://gateway.pinata.cloud/ipfs/YOUR_CID" # Pre-uploaded program - # onchain: true # true = onchain requests, false = offchain + # private_key: "PRIVATE_KEY" # Use env vars for secrets + # pinata_jwt: "PINATA_JWT" # For uploading programs + # program_url: "https://gateway.pinata.cloud/ipfs/QmNMRAB7DW43JSmENfzGmD96G6sqaeBBNfTVrrq5WQae3D" # Pre-uploaded program + # onchain: true # true = onchain requests, false = offchain nodes: cn1: diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index e98ab40896..f28f9759dd 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -26,8 +26,8 @@ program: # # Boundless configuration (for production) # boundless: # rpc_url: "https://sepolia.infura.io/v3/YOUR_KEY" - # private_key: "${PRIVATE_KEY}" # Use env vars for secrets - # pinata_jwt: "${PINATA_JWT}" # For uploading programs + # private_key: "PRIVATE_KEY" # Use env vars for secrets + # pinata_jwt: "PINATA_JWT" # For uploading programs # program_url: "https://gateway.pinata.cloud/ipfs/YOUR_CID" # Pre-uploaded program # onchain: true # true = onchain requests, false = offchain From 79dfa0769c70bda3e25b7be1dc455458a9bb6397 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Wed, 19 Nov 2025 19:03:12 +0500 Subject: [PATCH 03/10] chore: update script --- deploy/local/contracts.sh | 6 +++++- deploy/local/nodes.sh | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/deploy/local/contracts.sh b/deploy/local/contracts.sh index 8df4747723..0598ffa36f 100755 --- a/deploy/local/contracts.sh +++ b/deploy/local/contracts.sh @@ -4,7 +4,7 @@ # cargo install --locked --path ./crates/cli --bin enclave -f # Deploy CRISP Contracts -(cd examples/CRISP/packages/crisp-contracts && USE_MOCK_VERIFIER=true pnpm deploy:contracts:full --network localhost) +(cd examples/CRISP/packages/crisp-contracts && pnpm deploy:contracts:full --network localhost) # Add Ciphernodes to Enclave sleep 2 # wait for enclave to start @@ -13,11 +13,15 @@ sleep 2 # wait for enclave to start CN1=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 CN2=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC CN3=0x90F79bf6EB2c4f870365E785982E1f101E93b906 +CN4=0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 +CN5=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc # Add the ciphernodes to the enclave (cd examples/CRISP/packages/crisp-contracts && pnpm ciphernode:add --ciphernode-address "$CN1" --network "localhost") (cd examples/CRISP/packages/crisp-contracts && pnpm ciphernode:add --ciphernode-address "$CN2" --network "localhost") (cd examples/CRISP/packages/crisp-contracts && pnpm ciphernode:add --ciphernode-address "$CN3" --network "localhost") +(cd examples/CRISP/packages/crisp-contracts && pnpm ciphernode:add --ciphernode-address "$CN4" --network "localhost") +(cd examples/CRISP/packages/crisp-contracts && pnpm ciphernode:add --ciphernode-address "$CN5" --network "localhost") # Delete local DB diff --git a/deploy/local/nodes.sh b/deploy/local/nodes.sh index d7deaa9d1a..7b4e091fdc 100755 --- a/deploy/local/nodes.sh +++ b/deploy/local/nodes.sh @@ -12,4 +12,6 @@ concurrently \ enclave wallet set --name cn1 --private-key "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" && enclave wallet set --name cn2 --private-key "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" && enclave wallet set --name cn3 --private-key "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" && + enclave wallet set --name cn4 --private-key "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" && + enclave wallet set --name cn5 --private-key "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba" && enclave nodes up -v" \ No newline at end of file From d73486661377c59aa4c40c7b8ea43a6c8dafe989 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 21 Nov 2025 20:33:33 +0500 Subject: [PATCH 04/10] fix: add handle for failed E3 compute request --- crates/support/app/src/main.rs | 85 ++++-- crates/support/host/src/lib.rs | 254 +++++++++++++----- crates/support/types/src/lib.rs | 10 + .../crisp-contracts/lib/risc0-ethereum | 2 +- examples/CRISP/server/src/server/models.rs | 21 +- .../CRISP/server/src/server/routes/state.rs | 47 +++- 6 files changed, 325 insertions(+), 94 deletions(-) diff --git a/crates/support/app/src/main.rs b/crates/support/app/src/main.rs index 71d33f976a..fcbca42462 100644 --- a/crates/support/app/src/main.rs +++ b/crates/support/app/src/main.rs @@ -6,7 +6,7 @@ use actix_web::{middleware::Logger, web, App, HttpResponse, HttpServer, Result as ActixResult}; use e3_compute_provider::FHEInputs; -use e3_support_types::{ComputeRequest, WebhookPayload}; +use e3_support_types::{ComputationStatus, ComputeRequest, WebhookPayload}; use serde::Serialize; #[derive(Serialize, Debug)] @@ -18,14 +18,18 @@ struct ProcessingResponse { async fn call_webhook( callback_url: &str, e3_id: u64, + status: ComputationStatus, proof: Vec, ciphertext: Vec, + error: Option, ) -> anyhow::Result<()> { - println!("call_webhook()"); + println!("call_webhook() - status: {:?}", status); let payload = WebhookPayload { e3_id, + status, ciphertext, proof, + error, }; println!("callback_url: {}", callback_url); reqwest::Client::new() @@ -40,23 +44,27 @@ async fn call_webhook( async fn run_computation_async(fhe_inputs: FHEInputs) -> anyhow::Result<(Vec, Vec)> { println!("running computation..."); - let (boundless_output, ciphertext) = - tokio::task::spawn_blocking(move || e3_support_host::run_compute(fhe_inputs)).await??; - println!("have result from computation!"); - let proof: Vec = boundless_output.seal.into(); - Ok((proof, ciphertext)) -} - -async fn handle_webhook_delivery( - e3_id: u64, - callback_url: &str, - proof: Vec, - ciphertext: Vec, -) -> anyhow::Result<()> { - println!("handle_webhook_delivery()"); - call_webhook(callback_url, e3_id, proof, ciphertext).await?; - println!("✓ Webhook sent successfully for E3 {}", e3_id); - Ok(()) + let result = tokio::task::spawn_blocking(move || e3_support_host::run_compute(fhe_inputs)).await?; + + match result { + Ok((boundless_output, ciphertext)) => { + match boundless_output { + e3_support_host::BoundlessOutput::Success { seal, .. } => { + println!("have result from computation!"); + Ok((seal, ciphertext)) + } + e3_support_host::BoundlessOutput::Error { error } => { + Err(anyhow::anyhow!("Boundless request failed: {}", error)) + } + } + } + Err(e3_support_host::ComputeError::BoundlessFailed(msg)) => { + Err(anyhow::anyhow!("Boundless request failed: {}", msg)) + } + Err(e3_support_host::ComputeError::Other(msg)) => { + Err(anyhow::anyhow!("Computation error: {}", msg)) + } + } } async fn process_computation_background( @@ -64,12 +72,39 @@ async fn process_computation_background( callback_url: &str, fhe_inputs: FHEInputs, ) -> anyhow::Result<()> { - let (proof, ciphertext) = run_computation_async(fhe_inputs).await?; - println!("computation finished!"); - println!("handling webhook delivery..."); - handle_webhook_delivery(e3_id, callback_url, proof, ciphertext).await?; - println!("✓ Computation completed for E3 {}", e3_id); - Ok(()) + match run_computation_async(fhe_inputs).await { + Ok((proof, ciphertext)) => { + println!("computation finished!"); + println!("handling webhook delivery..."); + call_webhook( + callback_url, + e3_id, + ComputationStatus::Completed, + proof, + ciphertext, + None, + ) + .await?; + println!("Computation completed for E3 {}", e3_id); + Ok(()) + } + Err(e) => { + let error_msg = e.to_string(); + eprintln!("Computation failed for E3 {}: {}", e3_id, error_msg); + + call_webhook( + callback_url, + e3_id, + ComputationStatus::Failed, + vec![], + vec![], + Some(format!("Compute failed: {}", error_msg)), + ) + .await?; + + Err(e) + } + } } async fn handle_compute(req: web::Json) -> ActixResult { diff --git a/crates/support/host/src/lib.rs b/crates/support/host/src/lib.rs index 5b6b383a7e..81d81271f9 100644 --- a/crates/support/host/src/lib.rs +++ b/crates/support/host/src/lib.rs @@ -4,58 +4,79 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use anyhow::{Context, Result}; +use alloy_primitives::utils::parse_ether; +use alloy_signer_local::PrivateKeySigner; +use anyhow::{Context, Error, Result}; use bincode::serialize; +use boundless_market::{ + client::ClientError, + contracts::{boundless_market::MarketError, FulfillmentData}, + request_builder::OfferParams, + storage::storage_provider_from_env, + Client, +}; use e3_compute_provider::{ ComputeInput, ComputeManager, ComputeProvider, ComputeResult, FHEInputs, }; -use alloy_signer_local::PrivateKeySigner; use e3_user_program::fhe_processor; use methods::PROGRAM_ELF; -use boundless_market::{Client, storage::storage_provider_from_env, contracts::FulfillmentData}; -use url::Url; -use serde::{Deserialize, Serialize}; use std::time::{Duration, Instant}; +use url::Url; pub struct BoundlessProvider; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BoundlessOutput { - pub result: ComputeResult, - pub bytes: Vec, - pub seal: Vec, +#[derive(Debug, Clone)] +pub enum BoundlessOutput { + Success { + result: ComputeResult, + bytes: Vec, + seal: Vec, + }, + Error { + error: String, + }, +} + +#[derive(Debug)] +pub enum ComputeError { + BoundlessFailed(String), + Other(String), } impl ComputeProvider for BoundlessProvider { type Output = BoundlessOutput; fn prove(&self, input: &ComputeInput) -> Self::Output { - let is_dev_mode = std::env::var("RISC0_DEV_MODE") - .unwrap_or_else(|_| "0".to_string()) == "1"; - + let is_dev_mode = + std::env::var("RISC0_DEV_MODE").unwrap_or_else(|_| "0".to_string()) == "1"; + if is_dev_mode { println!("Dev mode: Using fake proof"); fake_prove(input) } else { println!("Using Boundless for proving"); - tokio::runtime::Handle::current() - .block_on(boundless_prove(input)) - .expect("Boundless proving failed") + tokio::runtime::Handle::current().block_on(boundless_prove(input)) } } } +fn encode_input(input: &[u8]) -> Result, Error> { + Ok(bytemuck::pod_collect_to_vec(&risc0_zkvm::serde::to_vec( + input, + )?)) +} + /// Dev mode: return fake proof without executing fn fake_prove(input: &ComputeInput) -> BoundlessOutput { println!("Generating fake proof for dev mode"); - + // Execute the program with the input let result = input.process(fhe_processor); - + // Serialize the result as journal bytes let journal_bytes = bincode::serialize(&result).unwrap_or_default(); - - BoundlessOutput { + + BoundlessOutput::Success { result, bytes: journal_bytes, seal: vec![], // No seal in dev mode @@ -63,86 +84,189 @@ fn fake_prove(input: &ComputeInput) -> BoundlessOutput { } /// Boundless proving -async fn boundless_prove(input: &ComputeInput) -> Result { +async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { println!("Submitting proof request to Boundless..."); - let rpc_url = std::env::var("RPC_URL") - .context("RPC_URL not set")? - .parse() - .context("Invalid RPC_URL")?; - - let private_key: PrivateKeySigner = std::env::var("PRIVATE_KEY") - .context("PRIVATE_KEY not set")? - .parse() - .context("Invalid PRIVATE_KEY")?; - - let client = Client::builder() + let rpc_url = match std::env::var("RPC_URL") + .context("RPC_URL not set") + .and_then(|url| url.parse().context("Invalid RPC_URL")) + { + Ok(url) => url, + Err(e) => { + return BoundlessOutput::Error { + error: e.to_string(), + } + } + }; + + let private_key: PrivateKeySigner = match std::env::var("PRIVATE_KEY") + .context("PRIVATE_KEY not set") + .and_then(|key| key.parse().context("Invalid PRIVATE_KEY")) + { + Ok(key) => key, + Err(e) => { + return BoundlessOutput::Error { + error: e.to_string(), + } + } + }; + + let storage_provider = match storage_provider_from_env() { + Ok(provider) => Some(provider), + Err(e) => { + eprintln!("Warning: Failed to get storage provider: {}", e); + None + } + }; + + let client = match Client::builder() .with_rpc_url(rpc_url) .with_private_key(private_key) - .with_storage_provider(Some(storage_provider_from_env()?)) + .with_storage_provider(storage_provider) .build() .await - .context("Failed to build Boundless client")?; + { + Ok(client) => client, + Err(e) => { + return BoundlessOutput::Error { + error: format!("Failed to build Boundless client: {}", e), + } + } + }; - let input_bytes = serialize(input)?; + let input_bytes = match encode_input(&serialize(input).unwrap()) { + Ok(bytes) => bytes, + Err(e) => { + return BoundlessOutput::Error { + error: format!("Failed to encode input: {}", e), + } + } + }; let program_url = std::env::var("PROGRAM_URL").ok(); - + let request = if let Some(url) = program_url { println!("Using pre-uploaded program: {}", url); - client.new_request() - .with_program_url(url.parse::().context("Failed to parse program URL")?) - .context("Failed to create new request")? - .with_stdin(input_bytes) + let parsed_url = match url.parse::() { + Ok(url) => url, + Err(e) => { + return BoundlessOutput::Error { + error: format!("Failed to parse program URL: {}", e), + } + } + }; + match client + .new_request() + .with_program_url(parsed_url) + .context("Failed to create new request") + { + Ok(req) => req.with_stdin(input_bytes).with_offer( + /// This auction begins with a flat period, allowing early bidding before the ramp-up begins. + /// The price then increases linearly to 0.03 ETH over 2 mins. + /// The maximum price of 0.03 ETH remains for 8 mins, + /// after which the price drops to 0 ETH for the expiry period of 10 mins. + OfferParams::builder() + .min_price(parse_ether("0.001").unwrap()) // Minimum price in ETH + .max_price(parse_ether("0.03").unwrap()) // Maximum price in ETH + .timeout(20 * 60) // Total timeout in seconds (20 minutes) + .lock_timeout(10 * 60) // Lock timeout in seconds (10 minutes) + .ramp_up_period(2 * 60), // Ramp up period in seconds (2 minutes) + ), + Err(e) => { + return BoundlessOutput::Error { + error: e.to_string(), + } + } + } } else { - println!("Warning: Uploading {}MB program at runtime", PROGRAM_ELF.len() / 1_000_000); - client.new_request() + println!( + "Warning: Uploading {}MB program at runtime", + PROGRAM_ELF.len() / 1_000_000 + ); + client + .new_request() .with_program(PROGRAM_ELF) .with_stdin(input_bytes) }; - let onchain = std::env::var("BOUNDLESS_ONCHAIN") - .unwrap_or_else(|_| "true".to_string()) == "true"; - - let (request_id, expires_at) = if onchain { + let onchain = + std::env::var("BOUNDLESS_ONCHAIN").unwrap_or_else(|_| "true".to_string()) == "true"; + + let (request_id, expires_at) = match if onchain { println!("Submitting onchain..."); - client.submit_onchain(request).await? + client.submit_onchain(request).await } else { println!("Submitting offchain..."); - client.submit_offchain(request).await? + client.submit_offchain(request).await + } { + Ok(result) => result, + Err(e) => { + return BoundlessOutput::Error { + error: format!("Failed to submit request: {}", e), + } + } }; println!("Request ID: {:x}, waiting for fulfillment...", request_id); - let fulfillment = client - .wait_for_request_fulfillment( - request_id, - Duration::from_secs(5), - expires_at, - ) + let fulfillment = match client + .wait_for_request_fulfillment(request_id, Duration::from_secs(5), expires_at) .await - .context("Failed to wait for fulfillment")?; + { + Ok(fulfillment) => fulfillment, + Err(ClientError::MarketError(MarketError::RequestHasExpired(_))) => { + return BoundlessOutput::Error { + error: format!( + "Boundless request expired: no prover picked up the request. Request ID: {:x}", + request_id + ), + }; + } + Err(e) => { + return BoundlessOutput::Error { + error: format!("Failed to wait for fulfillment: {}", e), + }; + } + }; println!("Proof received from Boundless!"); let data = fulfillment.data(); let (_, journal) = match data { Ok(FulfillmentData::ImageIdAndJournal(image_id, journal)) => (image_id, journal), - _ => return Err(anyhow::anyhow!("Invalid fulfillment data")), + _ => { + return BoundlessOutput::Error { + error: "Invalid fulfillment data".to_string(), + } + } }; - let decoded_journal: ComputeResult = bincode::deserialize(&journal) - .context("Failed to decode journal")?; + let decoded_journal: ComputeResult = match bincode::deserialize(&journal) { + Ok(result) => result, + Err(e) => { + return BoundlessOutput::Error { + error: format!("Failed to decode journal: {}", e), + } + } + }; - Ok(BoundlessOutput { + BoundlessOutput::Success { result: decoded_journal, bytes: journal.to_vec(), seal: fulfillment.seal.to_vec(), - }) + } } -pub fn run_compute(params: FHEInputs) -> Result<(BoundlessOutput, Vec)> { +pub fn run_compute( + params: FHEInputs, +) -> std::result::Result<(BoundlessOutput, Vec), ComputeError> { let boundless_provider = BoundlessProvider; - let mut provider = ComputeManager::new(boundless_provider, params, fhe_processor, false, None); + let mut provider = ComputeManager::new( + boundless_provider, + params.clone(), + fhe_processor, + false, + None, + ); // Start timer let start_time = Instant::now(); @@ -161,5 +285,9 @@ pub fn run_compute(params: FHEInputs) -> Result<(BoundlessOutput, Vec)> { minutes, seconds ); - Ok(output) + // Check if the output indicates failure + match output.0 { + BoundlessOutput::Success { .. } => Ok(output), + BoundlessOutput::Error { error } => Err(ComputeError::BoundlessFailed(error)), + } } diff --git a/crates/support/types/src/lib.rs b/crates/support/types/src/lib.rs index 2b3e1bfac4..2ef89cd432 100644 --- a/crates/support/types/src/lib.rs +++ b/crates/support/types/src/lib.rs @@ -23,13 +23,23 @@ pub struct ComputeRequest { pub callback_url: Option, } +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum ComputationStatus { + Completed, + Failed, +} + #[derive(Serialize, Debug)] pub struct WebhookPayload { pub e3_id: u64, + pub status: ComputationStatus, #[serde(serialize_with = "serialize_as_hex")] pub ciphertext: Vec, #[serde(serialize_with = "serialize_as_hex")] pub proof: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, } fn serialize_as_hex(bytes: &Vec, serializer: S) -> Result diff --git a/examples/CRISP/packages/crisp-contracts/lib/risc0-ethereum b/examples/CRISP/packages/crisp-contracts/lib/risc0-ethereum index d04a053901..32aa0b6f23 160000 --- a/examples/CRISP/packages/crisp-contracts/lib/risc0-ethereum +++ b/examples/CRISP/packages/crisp-contracts/lib/risc0-ethereum @@ -1 +1 @@ -Subproject commit d04a053901c9cb4e0f6cc599d5b4f21dccdbb3f8 +Subproject commit 32aa0b6f23ddd02dd93fc71717667606e5c7db86 diff --git a/examples/CRISP/server/src/server/models.rs b/examples/CRISP/server/src/server/models.rs index 7c7ca59c5b..96b155f288 100644 --- a/examples/CRISP/server/src/server/models.rs +++ b/examples/CRISP/server/src/server/models.rs @@ -12,12 +12,15 @@ use serde::{Deserialize, Deserializer, Serialize}; #[derivative(Debug)] pub struct WebhookPayload { pub e3_id: u64, - #[serde(deserialize_with = "deserialize_hex_string")] + pub status: String, // "completed" or "failed" + #[serde(deserialize_with = "deserialize_hex_string_optional", default)] #[derivative(Debug = "ignore")] pub ciphertext: Vec, - #[serde(deserialize_with = "deserialize_hex_string")] + #[serde(deserialize_with = "deserialize_hex_string_optional", default)] #[derivative(Debug = "ignore")] pub proof: Vec, + #[serde(default)] + pub error: Option, } pub fn deserialize_hex_string<'de, D>(deserializer: D) -> Result, D::Error> @@ -29,6 +32,20 @@ where hex::decode(hex_str).map_err(serde::de::Error::custom) } +pub fn deserialize_hex_string_optional<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let s: Option = Option::deserialize(deserializer)?; + match s { + Some(hex_str) => { + let hex_str = hex_str.strip_prefix("0x").unwrap_or(&hex_str); + hex::decode(hex_str).map_err(serde::de::Error::custom) + } + None => Ok(Vec::new()), + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct JsonResponse { pub response: String, diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index 26cbec7cf2..fabf7bbcc0 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -41,10 +41,51 @@ async fn handle_program_server_result(data: web::Json) -> impl R let incoming = data.into_inner(); info!( - "Received program server result for E3 ID: {:?}", - incoming.e3_id + "Received program server result for E3 ID: {:?}, status: {:?}", + incoming.e3_id, incoming.status ); + // Handle failed computation + if incoming.status == "failed" { + let error_msg = incoming + .error + .unwrap_or_else(|| "Unknown error".to_string()); + error!( + "Computation failed for E3 ID: {}. Error: {}", + incoming.e3_id, error_msg + ); + + // TODO: Update E3 state to indicate computation failed + // TODO: Handle ciphernode rewards for partial work + // TODO: Emit on-chain event if needed + + return HttpResponse::Ok().json(format!( + "Computation failed for E3 ID: {}. Error: {}", + incoming.e3_id, error_msg + )); + } + + // Handle successful computation + if incoming.status != "completed" { + error!( + "Unknown status '{}' for E3 ID: {}", + incoming.status, incoming.e3_id + ); + return HttpResponse::BadRequest().json(format!("Unknown status: {}", incoming.status)); + } + + // Validate that we have ciphertext and proof for completed status + if incoming.ciphertext.is_empty() || incoming.proof.is_empty() { + error!( + "Missing ciphertext or proof for completed computation E3 ID: {}", + incoming.e3_id + ); + return HttpResponse::BadRequest().json(format!( + "Missing ciphertext or proof for E3 ID: {}", + incoming.e3_id + )); + } + // Create the contract let contract: EnclaveContract = match EnclaveContractFactory::create_write( &CONFIG.http_rpc_url, @@ -55,7 +96,7 @@ async fn handle_program_server_result(data: web::Json) -> impl R { Ok(contract) => contract, Err(e) => { - info!("Failed to create contract: {:?}", e); + error!("Failed to create contract: {:?}", e); return HttpResponse::InternalServerError() .json(format!("Failed to create contract: {}", e)); } From ef6bb1f671880b84e8b0e1029268ff3e66cb2428 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 21 Nov 2025 22:02:02 +0500 Subject: [PATCH 05/10] fix: add ZKC collateral --- crates/support/host/src/lib.rs | 13 +++++++------ examples/CRISP/server/src/server/routes/state.rs | 13 ++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/support/host/src/lib.rs b/crates/support/host/src/lib.rs index 81d81271f9..26648c968a 100644 --- a/crates/support/host/src/lib.rs +++ b/crates/support/host/src/lib.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use alloy_primitives::utils::parse_ether; +use alloy_primitives::utils::{parse_ether, parse_units}; use alloy_signer_local::PrivateKeySigner; use anyhow::{Context, Error, Result}; use bincode::serialize; @@ -160,16 +160,17 @@ async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { .context("Failed to create new request") { Ok(req) => req.with_stdin(input_bytes).with_offer( - /// This auction begins with a flat period, allowing early bidding before the ramp-up begins. - /// The price then increases linearly to 0.03 ETH over 2 mins. - /// The maximum price of 0.03 ETH remains for 8 mins, - /// after which the price drops to 0 ETH for the expiry period of 10 mins. + // This auction begins with a flat period, allowing early bidding before the ramp-up begins. + // The price then increases linearly to 0.03 ETH over 2 mins. + // The maximum price of 0.03 ETH remains for 8 mins, + // after which the price drops to 0 ETH for the expiry period of 10 mins. OfferParams::builder() .min_price(parse_ether("0.001").unwrap()) // Minimum price in ETH .max_price(parse_ether("0.03").unwrap()) // Maximum price in ETH .timeout(20 * 60) // Total timeout in seconds (20 minutes) .lock_timeout(10 * 60) // Lock timeout in seconds (10 minutes) - .ramp_up_period(2 * 60), // Ramp up period in seconds (2 minutes) + .ramp_up_period(2 * 60) // Ramp up period in seconds (2 minutes) + .lock_collateral(parse_units("5", 18).unwrap()), // 5 ZKC ), Err(e) => { return BoundlessOutput::Error { diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index fabf7bbcc0..9ab74c4a7d 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -74,16 +74,15 @@ async fn handle_program_server_result(data: web::Json) -> impl R return HttpResponse::BadRequest().json(format!("Unknown status: {}", incoming.status)); } - // Validate that we have ciphertext and proof for completed status - if incoming.ciphertext.is_empty() || incoming.proof.is_empty() { + // Validate that we have ciphertext for completed status + // Proof is optional in dev mode + if incoming.ciphertext.is_empty() { error!( - "Missing ciphertext or proof for completed computation E3 ID: {}", + "Missing ciphertext for completed computation E3 ID: {}", incoming.e3_id ); - return HttpResponse::BadRequest().json(format!( - "Missing ciphertext or proof for E3 ID: {}", - incoming.e3_id - )); + return HttpResponse::BadRequest() + .json(format!("Missing ciphertext for E3 ID: {}", incoming.e3_id)); } // Create the contract From 80884e0b087a665cde45e10939626fc6ebb540ce Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 21 Nov 2025 22:16:57 +0500 Subject: [PATCH 06/10] chore: refator --- crates/support/host/src/lib.rs | 133 ++++++++++++--------------------- 1 file changed, 46 insertions(+), 87 deletions(-) diff --git a/crates/support/host/src/lib.rs b/crates/support/host/src/lib.rs index 26648c968a..bede4866b7 100644 --- a/crates/support/host/src/lib.rs +++ b/crates/support/host/src/lib.rs @@ -83,33 +83,31 @@ fn fake_prove(input: &ComputeInput) -> BoundlessOutput { } } -/// Boundless proving +fn to_output_error(e: E) -> BoundlessOutput { + BoundlessOutput::Error { + error: e.to_string(), + } +} + async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { + match boundless_prove_inner(input).await { + Ok(output) => output, + Err(e) => to_output_error(e), + } +} + +async fn boundless_prove_inner(input: &ComputeInput) -> Result { println!("Submitting proof request to Boundless..."); - let rpc_url = match std::env::var("RPC_URL") - .context("RPC_URL not set") - .and_then(|url| url.parse().context("Invalid RPC_URL")) - { - Ok(url) => url, - Err(e) => { - return BoundlessOutput::Error { - error: e.to_string(), - } - } - }; + let rpc_url = std::env::var("RPC_URL") + .context("RPC_URL not set")? + .parse() + .context("Invalid RPC_URL")?; - let private_key: PrivateKeySigner = match std::env::var("PRIVATE_KEY") - .context("PRIVATE_KEY not set") - .and_then(|key| key.parse().context("Invalid PRIVATE_KEY")) - { - Ok(key) => key, - Err(e) => { - return BoundlessOutput::Error { - error: e.to_string(), - } - } - }; + let private_key: PrivateKeySigner = std::env::var("PRIVATE_KEY") + .context("PRIVATE_KEY not set")? + .parse() + .context("Invalid PRIVATE_KEY")?; let storage_provider = match storage_provider_from_env() { Ok(provider) => Some(provider), @@ -119,47 +117,30 @@ async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { } }; - let client = match Client::builder() + let client = Client::builder() .with_rpc_url(rpc_url) .with_private_key(private_key) .with_storage_provider(storage_provider) .build() .await - { - Ok(client) => client, - Err(e) => { - return BoundlessOutput::Error { - error: format!("Failed to build Boundless client: {}", e), - } - } - }; + .context("Failed to build Boundless client")?; - let input_bytes = match encode_input(&serialize(input).unwrap()) { - Ok(bytes) => bytes, - Err(e) => { - return BoundlessOutput::Error { - error: format!("Failed to encode input: {}", e), - } - } - }; + let input_bytes = encode_input(&serialize(input).unwrap()) + .context("Failed to encode input")?; + let program_url = std::env::var("PROGRAM_URL").ok(); let request = if let Some(url) = program_url { println!("Using pre-uploaded program: {}", url); - let parsed_url = match url.parse::() { - Ok(url) => url, - Err(e) => { - return BoundlessOutput::Error { - error: format!("Failed to parse program URL: {}", e), - } - } - }; - match client + let parsed_url = url.parse::() + .context("Failed to parse program URL")?; + + client .new_request() .with_program_url(parsed_url) - .context("Failed to create new request") - { - Ok(req) => req.with_stdin(input_bytes).with_offer( + .context("Failed to create new request")? + .with_stdin(input_bytes) + .with_offer( // This auction begins with a flat period, allowing early bidding before the ramp-up begins. // The price then increases linearly to 0.03 ETH over 2 mins. // The maximum price of 0.03 ETH remains for 8 mins, @@ -171,13 +152,7 @@ async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { .lock_timeout(10 * 60) // Lock timeout in seconds (10 minutes) .ramp_up_period(2 * 60) // Ramp up period in seconds (2 minutes) .lock_collateral(parse_units("5", 18).unwrap()), // 5 ZKC - ), - Err(e) => { - return BoundlessOutput::Error { - error: e.to_string(), - } - } - } + ) } else { println!( "Warning: Uploading {}MB program at runtime", @@ -192,20 +167,14 @@ async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { let onchain = std::env::var("BOUNDLESS_ONCHAIN").unwrap_or_else(|_| "true".to_string()) == "true"; - let (request_id, expires_at) = match if onchain { + let (request_id, expires_at) = if onchain { println!("Submitting onchain..."); client.submit_onchain(request).await } else { println!("Submitting offchain..."); client.submit_offchain(request).await - } { - Ok(result) => result, - Err(e) => { - return BoundlessOutput::Error { - error: format!("Failed to submit request: {}", e), - } - } - }; + } + .context("Failed to submit request")?; println!("Request ID: {:x}, waiting for fulfillment...", request_id); @@ -215,18 +184,14 @@ async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { { Ok(fulfillment) => fulfillment, Err(ClientError::MarketError(MarketError::RequestHasExpired(_))) => { - return BoundlessOutput::Error { + return Ok(BoundlessOutput::Error { error: format!( "Boundless request expired: no prover picked up the request. Request ID: {:x}", request_id ), - }; - } - Err(e) => { - return BoundlessOutput::Error { - error: format!("Failed to wait for fulfillment: {}", e), - }; + }); } + Err(e) => return Err(e).context("Failed to wait for fulfillment")?, }; println!("Proof received from Boundless!"); @@ -234,26 +199,20 @@ async fn boundless_prove(input: &ComputeInput) -> BoundlessOutput { let (_, journal) = match data { Ok(FulfillmentData::ImageIdAndJournal(image_id, journal)) => (image_id, journal), _ => { - return BoundlessOutput::Error { + return Ok(BoundlessOutput::Error { error: "Invalid fulfillment data".to_string(), - } + }); } }; - let decoded_journal: ComputeResult = match bincode::deserialize(&journal) { - Ok(result) => result, - Err(e) => { - return BoundlessOutput::Error { - error: format!("Failed to decode journal: {}", e), - } - } - }; + let decoded_journal: ComputeResult = bincode::deserialize(&journal) + .context("Failed to decode journal")?; - BoundlessOutput::Success { + Ok(BoundlessOutput::Success { result: decoded_journal, bytes: journal.to_vec(), seal: fulfillment.seal.to_vec(), - } + }) } pub fn run_compute( From b50ab5152fd2b7f3d6de778d6f4f5e758c58c2c2 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 21 Nov 2025 22:44:45 +0500 Subject: [PATCH 07/10] chore: add logs --- crates/support/app/src/main.rs | 13 +++++++++++-- .../CRISP/server/src/server/routes/state.rs | 18 +++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/crates/support/app/src/main.rs b/crates/support/app/src/main.rs index fcbca42462..9a0f1d73e0 100644 --- a/crates/support/app/src/main.rs +++ b/crates/support/app/src/main.rs @@ -23,7 +23,12 @@ async fn call_webhook( ciphertext: Vec, error: Option, ) -> anyhow::Result<()> { - println!("call_webhook() - status: {:?}", status); + println!( + "call_webhook() - status: {:?}, ciphertext len: {}, proof len: {}", + status, + ciphertext.len(), + proof.len() + ); let payload = WebhookPayload { e3_id, status, @@ -50,7 +55,11 @@ async fn run_computation_async(fhe_inputs: FHEInputs) -> anyhow::Result<(Vec Ok((boundless_output, ciphertext)) => { match boundless_output { e3_support_host::BoundlessOutput::Success { seal, .. } => { - println!("have result from computation!"); + println!( + "have result from computation! seal len: {}, ciphertext len: {}", + seal.len(), + ciphertext.len() + ); Ok((seal, ciphertext)) } e3_support_host::BoundlessOutput::Error { error } => { diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index 9ab74c4a7d..435a329891 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -41,8 +41,8 @@ async fn handle_program_server_result(data: web::Json) -> impl R let incoming = data.into_inner(); info!( - "Received program server result for E3 ID: {:?}, status: {:?}", - incoming.e3_id, incoming.status + "Received program server result for E3 ID: {:?}, status: {:?}, ciphertext len: {}, proof len: {}", + incoming.e3_id, incoming.status, incoming.ciphertext.len(), incoming.proof.len() ); // Handle failed computation @@ -74,15 +74,15 @@ async fn handle_program_server_result(data: web::Json) -> impl R return HttpResponse::BadRequest().json(format!("Unknown status: {}", incoming.status)); } - // Validate that we have ciphertext for completed status - // Proof is optional in dev mode - if incoming.ciphertext.is_empty() { - error!( - "Missing ciphertext for completed computation E3 ID: {}", + if incoming.ciphertext.is_empty() && incoming.proof.is_empty() { + info!( + "Both ciphertext and proof are empty for E3 ID: {} - skipping chain publication", incoming.e3_id ); - return HttpResponse::BadRequest() - .json(format!("Missing ciphertext for E3 ID: {}", incoming.e3_id)); + return HttpResponse::Ok().json(format!( + "Computation completed for E3 ID: {}", + incoming.e3_id + )); } // Create the contract From d1ab393554eab5525f16c057a5810e1f984e5f7c Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 24 Nov 2025 02:41:45 +0500 Subject: [PATCH 08/10] fix: use the correct code file --- crates/program-server/src/lib.rs | 73 +++++++++++++++++++++++++----- crates/program-server/src/types.rs | 14 +++++- crates/support/app/src/main.rs | 19 ++++++-- 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/crates/program-server/src/lib.rs b/crates/program-server/src/lib.rs index bb175a1a99..4a10e6a098 100644 --- a/crates/program-server/src/lib.rs +++ b/crates/program-server/src/lib.rs @@ -11,7 +11,7 @@ use anyhow::Result; use e3_compute_provider::FHEInputs; use serde::Serialize; use std::{future::Future, pin::Pin, sync::Arc}; -use types::{ComputeRequest, WebhookPayload}; +use types::{ComputationStatus, ComputeRequest, WebhookPayload}; #[derive(Serialize, Debug)] struct ProcessingResponse { @@ -138,24 +138,44 @@ pub struct AppConfig { async fn call_webhook( callback_url: &str, e3_id: u64, + status: ComputationStatus, proof: Vec, ciphertext: Vec, + error: Option, ) -> Result<()> { - println!("call_webhook()"); + println!( + "call_webhook() - status: {:?}, ciphertext len: {}, proof len: {}", + status, + ciphertext.len(), + proof.len() + ); let payload = WebhookPayload { e3_id, + status, ciphertext, proof, + error, }; + println!("callback_url: {}", callback_url); - reqwest::Client::new() + let response = reqwest::Client::new() .post(callback_url) .json(&payload) .send() - .await? - .error_for_status()?; + .await?; + + println!("Webhook response status: {}", response.status()); + if !response.status().is_success() { + let error_body = response.text().await?; + println!("Webhook error response: {}", error_body); + return Err(anyhow::anyhow!( + "Webhook failed with status and body: {}", + error_body + )); + } + response.error_for_status()?; println!("✓ Webhook called successfully for E3 {}", e3_id); Ok(()) } @@ -163,11 +183,13 @@ async fn call_webhook( async fn handle_webhook_delivery( e3_id: u64, callback_url: &str, + status: ComputationStatus, proof: Vec, ciphertext: Vec, + error: Option, ) -> Result<()> { println!("handle_webhook_delivery()"); - call_webhook(callback_url, e3_id, proof, ciphertext).await?; + call_webhook(callback_url, e3_id, status, proof, ciphertext, error).await?; println!("✓ Webhook sent successfully for E3 {}", e3_id); Ok(()) } @@ -178,12 +200,39 @@ async fn process_computation_background( callback_url: &str, fhe_inputs: FHEInputs, ) -> Result<()> { - let (proof, ciphertext) = runner(fhe_inputs).await?; - println!("computation finished!"); - println!("handling webhook delivery..."); - handle_webhook_delivery(e3_id, callback_url, proof, ciphertext).await?; - println!("✓ Computation completed for E3 {}", e3_id); - Ok(()) + match runner(fhe_inputs).await { + Ok((proof, ciphertext)) => { + println!("computation finished!"); + println!("handling webhook delivery..."); + handle_webhook_delivery( + e3_id, + callback_url, + ComputationStatus::Completed, + proof, + ciphertext, + None, + ) + .await?; + println!("✓ Computation completed for E3 {}", e3_id); + Ok(()) + } + Err(e) => { + let error_msg = e.to_string(); + eprintln!("Computation failed for E3 {}: {}", e3_id, error_msg); + + handle_webhook_delivery( + e3_id, + callback_url, + ComputationStatus::Failed, + vec![], + vec![], + Some(format!("Compute failed: {}", error_msg)), + ) + .await?; + + Err(e) + } + } } async fn handle_compute( diff --git a/crates/program-server/src/types.rs b/crates/program-server/src/types.rs index 49c729fb14..1203fdb130 100644 --- a/crates/program-server/src/types.rs +++ b/crates/program-server/src/types.rs @@ -24,16 +24,26 @@ pub struct ComputeRequest { pub callback_url: Option, } +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum ComputationStatus { + Completed, + Failed, +} + #[derive(Derivative, Serialize)] #[derivative(Debug)] pub struct WebhookPayload { pub e3_id: u64, + pub status: ComputationStatus, #[serde(serialize_with = "serialize_as_hex")] #[derivative(Debug = "ignore")] pub ciphertext: Vec, #[serde(serialize_with = "serialize_as_hex")] #[derivative(Debug = "ignore")] pub proof: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, } fn serialize_as_hex(bytes: &Vec, serializer: S) -> Result @@ -71,7 +81,7 @@ where #[cfg(test)] mod tests { - use crate::{ComputeRequest, WebhookPayload}; + use crate::{types::ComputationStatus, ComputeRequest, WebhookPayload}; #[test] fn test_deserialize_compute_request() { @@ -145,6 +155,8 @@ mod tests { e3_id: 12345, ciphertext: vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], proof: vec![0xde, 0xad, 0xbe, 0xef], + status: ComputationStatus::Completed, + error: None, }; let json = serde_json::to_string(&payload).expect("Failed to serialize"); diff --git a/crates/support/app/src/main.rs b/crates/support/app/src/main.rs index 9a0f1d73e0..5919af8132 100644 --- a/crates/support/app/src/main.rs +++ b/crates/support/app/src/main.rs @@ -36,13 +36,26 @@ async fn call_webhook( proof, error, }; + + let json_payload = serde_json::to_string_pretty(&payload)?; + println!("Sending webhook payload:"); + println!("{}", json_payload); println!("callback_url: {}", callback_url); - reqwest::Client::new() + + let response = reqwest::Client::new() .post(callback_url) .json(&payload) .send() - .await? - .error_for_status()?; + .await?; + + println!("Webhook response status: {}", response.status()); + if !response.status().is_success() { + let error_body = response.text().await?; + println!("Webhook error response: {}", error_body); + return Err(anyhow::anyhow!("Webhook failed with status and body: {}", error_body)); + } + + response.error_for_status()?; println!("✓ Webhook called successfully for E3 {}", e3_id); Ok(()) } From dc6d29a380067b37582fe9dde00480db87db5a7f Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 24 Nov 2025 03:12:24 +0500 Subject: [PATCH 09/10] fix: fix test --- crates/program-server/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/program-server/src/types.rs b/crates/program-server/src/types.rs index 1203fdb130..be16e64278 100644 --- a/crates/program-server/src/types.rs +++ b/crates/program-server/src/types.rs @@ -160,7 +160,7 @@ mod tests { }; let json = serde_json::to_string(&payload).expect("Failed to serialize"); - let expected = r#"{"e3_id":12345,"ciphertext":"0x0123456789abcdef","proof":"0xdeadbeef"}"#; + let expected = r#"{"e3_id":12345,"status":"completed","ciphertext":"0x0123456789abcdef","proof":"0xdeadbeef"}"#; assert_eq!(json, expected); } From 214e622fdfb29a0ed13b0303953e23352eccac2a Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Mon, 24 Nov 2025 14:00:52 +0500 Subject: [PATCH 10/10] chore: refactor webhook payload to use Enum --- crates/program-server/src/lib.rs | 77 ++++----- crates/program-server/src/types.rs | 60 ++++--- examples/CRISP/server/src/server/models.rs | 42 ++--- .../CRISP/server/src/server/routes/state.rs | 162 +++++++++--------- 4 files changed, 159 insertions(+), 182 deletions(-) diff --git a/crates/program-server/src/lib.rs b/crates/program-server/src/lib.rs index 4a10e6a098..32b6ff89b0 100644 --- a/crates/program-server/src/lib.rs +++ b/crates/program-server/src/lib.rs @@ -11,7 +11,7 @@ use anyhow::Result; use e3_compute_provider::FHEInputs; use serde::Serialize; use std::{future::Future, pin::Pin, sync::Arc}; -use types::{ComputationStatus, ComputeRequest, WebhookPayload}; +use types::{ComputeRequest, WebhookPayload}; #[derive(Serialize, Debug)] struct ProcessingResponse { @@ -135,28 +135,27 @@ pub struct AppConfig { pub localhost_rewrite: Option, } -async fn call_webhook( - callback_url: &str, - e3_id: u64, - status: ComputationStatus, - proof: Vec, - ciphertext: Vec, - error: Option, -) -> Result<()> { - println!( - "call_webhook() - status: {:?}, ciphertext len: {}, proof len: {}", - status, - ciphertext.len(), - proof.len() - ); - let payload = WebhookPayload { - e3_id, - status, - ciphertext, - proof, - error, +async fn call_webhook(callback_url: &str, payload: WebhookPayload) -> Result<()> { + let e3_id = match &payload { + WebhookPayload::Completed { e3_id, .. } => *e3_id, + WebhookPayload::Failed { e3_id, .. } => *e3_id, }; + match &payload { + WebhookPayload::Completed { + ciphertext, proof, .. + } => { + println!( + "call_webhook() - status: Completed, ciphertext len: {}, proof len: {}", + ciphertext.len(), + proof.len() + ); + } + WebhookPayload::Failed { error, .. } => { + println!("call_webhook() - status: Failed, error: {}", error); + } + } + println!("callback_url: {}", callback_url); let response = reqwest::Client::new() @@ -180,17 +179,10 @@ async fn call_webhook( Ok(()) } -async fn handle_webhook_delivery( - e3_id: u64, - callback_url: &str, - status: ComputationStatus, - proof: Vec, - ciphertext: Vec, - error: Option, -) -> Result<()> { +async fn handle_webhook_delivery(callback_url: &str, payload: WebhookPayload) -> Result<()> { println!("handle_webhook_delivery()"); - call_webhook(callback_url, e3_id, status, proof, ciphertext, error).await?; - println!("✓ Webhook sent successfully for E3 {}", e3_id); + call_webhook(callback_url, payload).await?; + println!("✓ Webhook sent successfully"); Ok(()) } @@ -204,15 +196,12 @@ async fn process_computation_background( Ok((proof, ciphertext)) => { println!("computation finished!"); println!("handling webhook delivery..."); - handle_webhook_delivery( + let payload = WebhookPayload::Completed { e3_id, - callback_url, - ComputationStatus::Completed, - proof, ciphertext, - None, - ) - .await?; + proof, + }; + handle_webhook_delivery(callback_url, payload).await?; println!("✓ Computation completed for E3 {}", e3_id); Ok(()) } @@ -220,15 +209,11 @@ async fn process_computation_background( let error_msg = e.to_string(); eprintln!("Computation failed for E3 {}: {}", e3_id, error_msg); - handle_webhook_delivery( + let payload = WebhookPayload::Failed { e3_id, - callback_url, - ComputationStatus::Failed, - vec![], - vec![], - Some(format!("Compute failed: {}", error_msg)), - ) - .await?; + error: format!("Compute failed: {}", error_msg), + }; + handle_webhook_delivery(callback_url, payload).await?; Err(e) } diff --git a/crates/program-server/src/types.rs b/crates/program-server/src/types.rs index be16e64278..81996753c7 100644 --- a/crates/program-server/src/types.rs +++ b/crates/program-server/src/types.rs @@ -24,26 +24,25 @@ pub struct ComputeRequest { pub callback_url: Option, } -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] -pub enum ComputationStatus { - Completed, - Failed, -} - -#[derive(Derivative, Serialize)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative(Debug)] -pub struct WebhookPayload { - pub e3_id: u64, - pub status: ComputationStatus, - #[serde(serialize_with = "serialize_as_hex")] - #[derivative(Debug = "ignore")] - pub ciphertext: Vec, - #[serde(serialize_with = "serialize_as_hex")] - #[derivative(Debug = "ignore")] - pub proof: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, +#[serde(tag = "status", rename_all = "lowercase")] +pub enum WebhookPayload { + Completed { + e3_id: u64, + #[serde(serialize_with = "serialize_as_hex")] + #[serde(deserialize_with = "deserialize_hex_string")] + #[derivative(Debug = "ignore")] + ciphertext: Vec, + #[serde(serialize_with = "serialize_as_hex")] + #[serde(deserialize_with = "deserialize_hex_string")] + #[derivative(Debug = "ignore")] + proof: Vec, + }, + Failed { + e3_id: u64, + error: String, + }, } fn serialize_as_hex(bytes: &Vec, serializer: S) -> Result @@ -81,7 +80,7 @@ where #[cfg(test)] mod tests { - use crate::{types::ComputationStatus, ComputeRequest, WebhookPayload}; + use crate::{ComputeRequest, WebhookPayload}; #[test] fn test_deserialize_compute_request() { @@ -150,17 +149,28 @@ mod tests { } #[test] - fn test_webhook_payload_serialization() { - let payload = WebhookPayload { + fn test_webhook_payload_serialization_completed() { + let payload = WebhookPayload::Completed { e3_id: 12345, ciphertext: vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], proof: vec![0xde, 0xad, 0xbe, 0xef], - status: ComputationStatus::Completed, - error: None, }; let json = serde_json::to_string(&payload).expect("Failed to serialize"); - let expected = r#"{"e3_id":12345,"status":"completed","ciphertext":"0x0123456789abcdef","proof":"0xdeadbeef"}"#; + let expected = r#"{"status":"completed","e3_id":12345,"ciphertext":"0x0123456789abcdef","proof":"0xdeadbeef"}"#; + + assert_eq!(json, expected); + } + + #[test] + fn test_webhook_payload_serialization_failed() { + let payload = WebhookPayload::Failed { + e3_id: 12345, + error: "Computation failed".to_string(), + }; + + let json = serde_json::to_string(&payload).expect("Failed to serialize"); + let expected = r#"{"status":"failed","e3_id":12345,"error":"Computation failed"}"#; assert_eq!(json, expected); } diff --git a/examples/CRISP/server/src/server/models.rs b/examples/CRISP/server/src/server/models.rs index 96b155f288..cb2f3144b7 100644 --- a/examples/CRISP/server/src/server/models.rs +++ b/examples/CRISP/server/src/server/models.rs @@ -8,19 +8,23 @@ use anyhow::Result; use derivative::Derivative; use serde::{Deserialize, Deserializer, Serialize}; -#[derive(Derivative, Deserialize)] +#[derive(Derivative, Deserialize, Serialize)] #[derivative(Debug)] -pub struct WebhookPayload { - pub e3_id: u64, - pub status: String, // "completed" or "failed" - #[serde(deserialize_with = "deserialize_hex_string_optional", default)] - #[derivative(Debug = "ignore")] - pub ciphertext: Vec, - #[serde(deserialize_with = "deserialize_hex_string_optional", default)] - #[derivative(Debug = "ignore")] - pub proof: Vec, - #[serde(default)] - pub error: Option, +#[serde(tag = "status", rename_all = "lowercase")] +pub enum WebhookPayload { + Completed { + e3_id: u64, + #[serde(deserialize_with = "deserialize_hex_string")] + #[derivative(Debug = "ignore")] + ciphertext: Vec, + #[serde(deserialize_with = "deserialize_hex_string")] + #[derivative(Debug = "ignore")] + proof: Vec, + }, + Failed { + e3_id: u64, + error: String, + }, } pub fn deserialize_hex_string<'de, D>(deserializer: D) -> Result, D::Error> @@ -32,20 +36,6 @@ where hex::decode(hex_str).map_err(serde::de::Error::custom) } -pub fn deserialize_hex_string_optional<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s: Option = Option::deserialize(deserializer)?; - match s { - Some(hex_str) => { - let hex_str = hex_str.strip_prefix("0x").unwrap_or(&hex_str); - hex::decode(hex_str).map_err(serde::de::Error::custom) - } - None => Ok(Vec::new()), - } -} - #[derive(Debug, Deserialize, Serialize)] pub struct JsonResponse { pub response: String, diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index 435a329891..7ae055fc12 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -40,94 +40,86 @@ pub fn setup_routes(config: &mut web::ServiceConfig) { async fn handle_program_server_result(data: web::Json) -> impl Responder { let incoming = data.into_inner(); - info!( - "Received program server result for E3 ID: {:?}, status: {:?}, ciphertext len: {}, proof len: {}", - incoming.e3_id, incoming.status, incoming.ciphertext.len(), incoming.proof.len() - ); - - // Handle failed computation - if incoming.status == "failed" { - let error_msg = incoming - .error - .unwrap_or_else(|| "Unknown error".to_string()); - error!( - "Computation failed for E3 ID: {}. Error: {}", - incoming.e3_id, error_msg - ); - - // TODO: Update E3 state to indicate computation failed - // TODO: Handle ciphernode rewards for partial work - // TODO: Emit on-chain event if needed - - return HttpResponse::Ok().json(format!( - "Computation failed for E3 ID: {}. Error: {}", - incoming.e3_id, error_msg - )); - } - - // Handle successful computation - if incoming.status != "completed" { - error!( - "Unknown status '{}' for E3 ID: {}", - incoming.status, incoming.e3_id - ); - return HttpResponse::BadRequest().json(format!("Unknown status: {}", incoming.status)); - } - - if incoming.ciphertext.is_empty() && incoming.proof.is_empty() { - info!( - "Both ciphertext and proof are empty for E3 ID: {} - skipping chain publication", - incoming.e3_id - ); - return HttpResponse::Ok().json(format!( - "Computation completed for E3 ID: {}", - incoming.e3_id - )); - } - - // Create the contract - let contract: EnclaveContract = match EnclaveContractFactory::create_write( - &CONFIG.http_rpc_url, - &CONFIG.enclave_address, - &CONFIG.private_key, - ) - .await - { - Ok(contract) => contract, - Err(e) => { - error!("Failed to create contract: {:?}", e); - return HttpResponse::InternalServerError() - .json(format!("Failed to create contract: {}", e)); + match incoming { + WebhookPayload::Failed { e3_id, error } => { + error!("Computation failed for E3 ID: {}. Error: {}", e3_id, error); + + // TODO: Update E3 state to indicate computation failed + // TODO: Handle ciphernode rewards for partial work + // TODO: Emit on-chain event if needed + + HttpResponse::Ok().json(format!( + "Computation failed for E3 ID: {}. Error: {}", + e3_id, error + )) } - }; + WebhookPayload::Completed { + e3_id, + ciphertext, + proof, + } => { + info!( + "Received program server result for E3 ID: {}, ciphertext len: {}, proof len: {}", + e3_id, + ciphertext.len(), + proof.len() + ); + + // In dev mode, proof might be empty + if ciphertext.is_empty() && proof.is_empty() { + info!( + "Both ciphertext and proof are empty for E3 ID: {} - skipping chain publication", + e3_id + ); + return HttpResponse::Ok() + .json(format!("Computation completed for E3 ID: {}", e3_id)); + } - // Try the direct call - let tx_result = contract - .publish_ciphertext_output( - U256::from(incoming.e3_id), - Bytes::from(incoming.ciphertext.clone()), - Bytes::from(incoming.proof.clone()), - ) - .await; - - let pending_tx = match tx_result { - Ok(tx) => tx, - Err(e) => { - error!("Failed to send transaction: {:?}", e); - return HttpResponse::InternalServerError() - .json(format!("Failed to send transaction: {}", e)); + // Create the contract + let contract: EnclaveContract = match EnclaveContractFactory::create_write( + &CONFIG.http_rpc_url, + &CONFIG.enclave_address, + &CONFIG.private_key, + ) + .await + { + Ok(contract) => contract, + Err(e) => { + error!("Failed to create contract: {:?}", e); + return HttpResponse::InternalServerError() + .json(format!("Failed to create contract: {}", e)); + } + }; + + // Try the direct call + let tx_result = contract + .publish_ciphertext_output( + U256::from(e3_id), + Bytes::from(ciphertext.clone()), + Bytes::from(proof.clone()), + ) + .await; + + let pending_tx = match tx_result { + Ok(tx) => tx, + Err(e) => { + error!("Failed to send transaction: {:?}", e); + return HttpResponse::InternalServerError() + .json(format!("Failed to send transaction: {}", e)); + } + }; + + info!( + "Ciphertext output published successfully for E3 ID: {} with tx: {}", + e3_id, pending_tx.transaction_hash + ); + + HttpResponse::Ok().json(format!( + "Ciphertext output published successfully for E3 ID: {}", + e3_id + )) } - }; - - info!( - "Ciphertext output published successfully for E3 ID: {} with tx: {}", - incoming.e3_id, pending_tx.transaction_hash - ); - - HttpResponse::Ok().json(format!( - "Ciphertext output published successfully for E3 ID: {}", - incoming.e3_id - )) + } } /// Get the result for a given round