From 7a3b92078170574054730ee74b4aa8cebae41542 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 14 Feb 2023 16:00:28 -0800 Subject: [PATCH] Support proof generation from the wasm: 1. Refactor Cargo.toml to support non parallel wasm build 2. Force cannonical (de)serialization of usize to 8 bytes regardless of target 3. Add synthesizer and console to wasm to enable transaction, program, & circuit types --- algorithms/Cargo.toml | 4 + .../ahp/prover/round_functions/first.rs | 6 +- .../marlin/ahp/prover/round_functions/mod.rs | 2 +- algorithms/src/snark/marlin/marlin.rs | 8 + circuit/account/Cargo.toml | 4 + circuit/algorithms/Cargo.toml | 1 + circuit/collections/Cargo.toml | 4 + circuit/network/Cargo.toml | 4 + circuit/program/Cargo.toml | 5 + console/account/Cargo.toml | 1 + console/algorithms/Cargo.toml | 4 + console/collections/Cargo.toml | 1 + console/network/Cargo.toml | 4 + console/network/environment/Cargo.toml | 4 + console/program/Cargo.toml | 4 + parameters/Cargo.toml | 3 + r1cs/Cargo.toml | 1 + synthesizer/Cargo.toml | 9 +- synthesizer/src/block/transactions/mod.rs | 2 +- synthesizer/src/process/mod.rs | 18 ++ .../src/process/stack/inclusion/mod.rs | 166 ++++++++++++++++++ utilities/Cargo.toml | 1 + utilities/src/parallel.rs | 6 +- wasm/Cargo.toml | 9 +- wasm/src/lib.rs | 6 + 25 files changed, 266 insertions(+), 11 deletions(-) diff --git a/algorithms/Cargo.toml b/algorithms/Cargo.toml index 7071e74154..0ca64e4b1e 100644 --- a/algorithms/Cargo.toml +++ b/algorithms/Cargo.toml @@ -140,6 +140,10 @@ version = "1.0" version = "0.4" optional = true +[dependencies.web-sys] +version = "0.3" +features = ["console"] + [dev-dependencies.expect-test] version = "1.4.1" diff --git a/algorithms/src/snark/marlin/ahp/prover/round_functions/first.rs b/algorithms/src/snark/marlin/ahp/prover/round_functions/first.rs index c1b9c354b2..d865561563 100644 --- a/algorithms/src/snark/marlin/ahp/prover/round_functions/first.rs +++ b/algorithms/src/snark/marlin/ahp/prover/round_functions/first.rs @@ -71,13 +71,15 @@ impl AHPForR1CS { let round_time = start_timer!(|| "AHP::Prover::FirstRound"); let constraint_domain = state.constraint_domain; let batch_size = state.batch_size; - + let z_a = state.z_a.take().unwrap(); let z_b = state.z_b.take().unwrap(); let private_variables = core::mem::take(&mut state.private_variables); + assert_eq!(z_a.len(), batch_size); assert_eq!(z_b.len(), batch_size); assert_eq!(private_variables.len(), batch_size); + let mut r_b_s = Vec::with_capacity(batch_size); let mut job_pool = snarkvm_utilities::ExecutionPool::with_capacity(3 * batch_size); @@ -109,8 +111,8 @@ impl AHPForR1CS { assert_eq!(batches.len(), batch_size); let mask_poly = Self::calculate_mask_poly(constraint_domain, rng); - let oracles = prover::FirstOracles { batches, mask_poly }; + assert!(oracles.matches_info(&Self::first_round_polynomial_info(batch_size))); state.first_round_oracles = Some(Arc::new(oracles)); state.mz_poly_randomizer = MM::ZK.then_some(r_b_s); diff --git a/algorithms/src/snark/marlin/ahp/prover/round_functions/mod.rs b/algorithms/src/snark/marlin/ahp/prover/round_functions/mod.rs index b221302524..40afe2712a 100644 --- a/algorithms/src/snark/marlin/ahp/prover/round_functions/mod.rs +++ b/algorithms/src/snark/marlin/ahp/prover/round_functions/mod.rs @@ -19,9 +19,9 @@ use crate::snark::marlin::{ prover, MarlinMode, }; -use itertools::Itertools; use snarkvm_fields::PrimeField; use snarkvm_r1cs::ConstraintSynthesizer; +use itertools::Itertools; use snarkvm_utilities::cfg_iter; #[cfg(not(feature = "std"))] diff --git a/algorithms/src/snark/marlin/marlin.rs b/algorithms/src/snark/marlin/marlin.rs index 88ddb64355..a04ef9d664 100644 --- a/algorithms/src/snark/marlin/marlin.rs +++ b/algorithms/src/snark/marlin/marlin.rs @@ -142,6 +142,14 @@ impl, MM: MarlinMode> MarlinSNAR if terminator.load(Ordering::Relaxed) { Err(MarlinError::Terminated) } else { Ok(()) } } + fn to_le_bytes_8(n: &usize) -> [u8; 8] { + let mut bytes = [0u8; 8]; + let n_bytes = n.to_le_bytes(); + let len = std::mem::size_of::(); + bytes[..len].copy_from_slice(&n_bytes[..len]); + bytes + } + fn init_sponge( fs_parameters: &FS::Parameters, batch_size: usize, diff --git a/circuit/account/Cargo.toml b/circuit/account/Cargo.toml index c2f52236ce..bd5c09bf40 100644 --- a/circuit/account/Cargo.toml +++ b/circuit/account/Cargo.toml @@ -33,3 +33,7 @@ version = "1.0.70" [features] default = [ "enable_console" ] enable_console = [ "console" ] +parallel = [ + "snarkvm-circuit-algorithms/parallel", + "snarkvm-circuit-network/parallel" +] diff --git a/circuit/algorithms/Cargo.toml b/circuit/algorithms/Cargo.toml index 4dfbc723f4..561ae9367e 100644 --- a/circuit/algorithms/Cargo.toml +++ b/circuit/algorithms/Cargo.toml @@ -35,3 +35,4 @@ version = "1.0.70" [features] default = [ "enable_console" ] enable_console = [ "console" ] +parallel = [ "snarkvm-fields/parallel" ] diff --git a/circuit/collections/Cargo.toml b/circuit/collections/Cargo.toml index 7cb47fcb6a..40e4d387a2 100644 --- a/circuit/collections/Cargo.toml +++ b/circuit/collections/Cargo.toml @@ -22,12 +22,15 @@ version = "0.10.2" [dev-dependencies.snarkvm-circuit-network] path = "../network" +features = [ "parallel" ] [dev-dependencies.snarkvm-console-algorithms] path = "../../console/algorithms" +features = [ "parallel" ] [dev-dependencies.snarkvm-console-network] path = "../../console/network" +features = [ "parallel" ] [dev-dependencies.snarkvm-utilities] path = "../../utilities" @@ -38,3 +41,4 @@ version = "1.0.70" [features] default = [ "enable_console" ] enable_console = [ "console" ] +parallel = [ "snarkvm-circuit-algorithms/parallel" ] diff --git a/circuit/network/Cargo.toml b/circuit/network/Cargo.toml index de65c28fa3..715ecdf829 100644 --- a/circuit/network/Cargo.toml +++ b/circuit/network/Cargo.toml @@ -30,3 +30,7 @@ path = "../../console/types" [features] default = [ "enable_console" ] enable_console = [ "console" ] +parallel = [ + "snarkvm-circuit-algorithms/parallel", + "snarkvm-circuit-collections/parallel" +] diff --git a/circuit/program/Cargo.toml b/circuit/program/Cargo.toml index 68a73a04ed..1c1eaad3e1 100644 --- a/circuit/program/Cargo.toml +++ b/circuit/program/Cargo.toml @@ -49,3 +49,8 @@ version = "0.8" [features] default = [ "enable_console" ] enable_console = [ "console" ] +parallel = [ + "snarkvm-circuit-account/parallel", + "snarkvm-circuit-collections/parallel", + "snarkvm-circuit-network/parallel" +] diff --git a/console/account/Cargo.toml b/console/account/Cargo.toml index 4579af4652..f96196fc43 100644 --- a/console/account/Cargo.toml +++ b/console/account/Cargo.toml @@ -42,6 +42,7 @@ default = [ "signature", "view_key" ] +parallel = [ "snarkvm-console-network/parallel" ] compute_key = [ "private_key" ] graph_key = [ "private_key" ] private_key = [ "compute_key" ] diff --git a/console/algorithms/Cargo.toml b/console/algorithms/Cargo.toml index e21ff24878..ec02aeb4e3 100644 --- a/console/algorithms/Cargo.toml +++ b/console/algorithms/Cargo.toml @@ -58,3 +58,7 @@ version = "1.0" [dev-dependencies.serde_json] version = "1.0" features = [ "preserve_order" ] + +[features] +default = [ ] +parallel = [ "snarkvm-fields/parallel" ] diff --git a/console/collections/Cargo.toml b/console/collections/Cargo.toml index 3cf72195b0..9c42b4de73 100644 --- a/console/collections/Cargo.toml +++ b/console/collections/Cargo.toml @@ -30,6 +30,7 @@ version = "1" [dev-dependencies.snarkvm-console-network] path = "../network" +features = [ "parallel" ] [dev-dependencies.criterion] version = "0.4.0" diff --git a/console/network/Cargo.toml b/console/network/Cargo.toml index deca65a388..b35ae18a24 100644 --- a/console/network/Cargo.toml +++ b/console/network/Cargo.toml @@ -12,6 +12,10 @@ wasm = [ "snarkvm-algorithms/polycommit_wasm", "snarkvm-parameters/wasm" ] +parallel = [ + "snarkvm-console-collections/parallel", + "snarkvm-fields/parallel" +] [dependencies.snarkvm-algorithms] path = "../../algorithms" diff --git a/console/network/environment/Cargo.toml b/console/network/environment/Cargo.toml index f6ef21a505..317c548b50 100644 --- a/console/network/environment/Cargo.toml +++ b/console/network/environment/Cargo.toml @@ -41,3 +41,7 @@ default-features = false [dependencies.serde] version = "1.0" + +[features] +default = [ ] +parallel = [ "snarkvm-fields/parallel" ] \ No newline at end of file diff --git a/console/program/Cargo.toml b/console/program/Cargo.toml index fb5b4fb9c8..833b9410c4 100644 --- a/console/program/Cargo.toml +++ b/console/program/Cargo.toml @@ -9,6 +9,10 @@ edition = "2021" [features] default = [ ] test = [ ] +parallel = [ + "snarkvm-console-collections/parallel", + "snarkvm-console-network/parallel" +] [dependencies.snarkvm-console-account] path = "../account" diff --git a/parameters/Cargo.toml b/parameters/Cargo.toml index c80486ddfa..b0e1ecb06a 100644 --- a/parameters/Cargo.toml +++ b/parameters/Cargo.toml @@ -100,12 +100,15 @@ default-features = false [dev-dependencies.snarkvm-circuit] path = "../circuit" +features = [ "parallel" ] [dev-dependencies.snarkvm-console] path = "../console" +features = [ "parallel" ] [dev-dependencies.snarkvm-synthesizer] path = "../synthesizer" +features = [ "parallel" ] [dev-dependencies.curl] version = "0.4.34" diff --git a/r1cs/Cargo.toml b/r1cs/Cargo.toml index f4f73155d9..d15f0e6483 100644 --- a/r1cs/Cargo.toml +++ b/r1cs/Cargo.toml @@ -57,3 +57,4 @@ version = "1.0" [features] default = [ ] +parallel = [ "snarkvm-fields/parallel" ] diff --git a/synthesizer/Cargo.toml b/synthesizer/Cargo.toml index eb807cb3eb..3edda80b43 100644 --- a/synthesizer/Cargo.toml +++ b/synthesizer/Cargo.toml @@ -41,6 +41,9 @@ harness = false [features] default = [ ] +web = [ + "snarkvm-utilities/web" +] aleo-cli = [ ] cuda = [ "snarkvm-algorithms/cuda" ] serial = [ @@ -102,15 +105,15 @@ version = "2" version = "1.9" features = [ "serde", "rayon" ] -[dependencies.itertools] -version = "0.10.1" - [dependencies.once_cell] version = "1.17" [dependencies.parking_lot] version = "0.12" +[dependencies.itertools] +version = "0.10.3" + [dependencies.paste] version = "1.0.12" diff --git a/synthesizer/src/block/transactions/mod.rs b/synthesizer/src/block/transactions/mod.rs index 1ca821ccda..c9e071c23a 100644 --- a/synthesizer/src/block/transactions/mod.rs +++ b/synthesizer/src/block/transactions/mod.rs @@ -90,7 +90,7 @@ impl Transactions { pub fn find_transaction_for_serial_number(&self, serial_number: &Field) -> Option<&Transaction> { cfg_find!(self, serial_number, contains_serial_number) } - + /// Returns the transaction with the given commitment, if it exists. pub fn find_transaction_for_commitment(&self, commitment: &Field) -> Option<&Transaction> { cfg_find!(self, commitment, contains_commitment) diff --git a/synthesizer/src/process/mod.rs b/synthesizer/src/process/mod.rs index 0d06d79d70..0c14b1df16 100644 --- a/synthesizer/src/process/mod.rs +++ b/synthesizer/src/process/mod.rs @@ -149,6 +149,24 @@ impl Process { Ok(process) } + #[inline] + pub fn load_wasm() -> Result { + // Initialize the process. + let mut process = Self { universal_srs: Arc::new(UniversalSRS::load()?), stacks: IndexMap::new() }; + + // Initialize the 'credits.aleo' program. + let program = Program::credits()?; + + // Compute the 'credits.aleo' program stack. + let stack = Stack::new(&process, &program)?; + + // Add the stack to the process. + process.stacks.insert(*program.id(), stack); + + // Return the process. + Ok(process) + } + /// Initializes a new process with a cache of previously used keys. This version is suitable for tests /// (which often use nested loops that keep reusing those), as their deserialization is slow. #[cfg(test)] diff --git a/synthesizer/src/process/stack/inclusion/mod.rs b/synthesizer/src/process/stack/inclusion/mod.rs index 0be9b2cb2f..05b3872b02 100644 --- a/synthesizer/src/process/stack/inclusion/mod.rs +++ b/synthesizer/src/process/stack/inclusion/mod.rs @@ -286,6 +286,105 @@ impl Inclusion { Ok((assignments, global_state_root)) } + /// Returns the inclusion assignments for the given execution. + pub fn prepare_execution_stateless( + &self, + execution: &Execution, + global_state_root: N::StateRoot, + commitment_to_state_path: HashMap> + ) -> Result<(Vec>, N::StateRoot)> { + // Ensure the number of leaves is within the Merkle tree size. + Transaction::check_execution_size(execution)?; + + // Ensure the inclusion proof in the execution is 'None'. + if execution.inclusion_proof().is_some() { + bail!("Inclusion proof in the execution should not be set in 'Inclusion::prepare_execution'") + } + + // Initialize an empty transaction tree. + let mut transaction_tree = N::merkle_tree_bhp::(&[])?; + // Initialize a vector for the assignments. + let mut assignments = vec![]; + + // Ensure the global state root is not zero. + if *global_state_root == Field::zero() { + bail!("Inclusion expected the global state root in the execution to *not* be zero") + } + + for (transition_index, transition) in execution.transitions().enumerate() { + // Construct the transaction leaf. + let transaction_leaf = TransactionLeaf::new_execution(transition_index as u16, **transition.id()); + + // Process the input tasks. + match self.input_tasks.get(transition.id()) { + Some(tasks) => { + for task in tasks { + // Retrieve the local state root. + let local_state_root = (*transaction_tree.root()).into(); + + // Construct the state path. + let state_path = commitment_to_state_path.get(&task.commitment.to_string()).unwrap(); + + // Ensure the global state root is the same across iterations. + if global_state_root != state_path.global_state_root() { + bail!("Inclusion expected the global state root to be the same across iterations") + } + + // Construct the assignment for the state path. + let assignment = InclusionAssignment::new( + state_path.clone(), + task.commitment, + task.gamma, + task.serial_number, + local_state_root, + !task.is_local, + ); + + // Add the assignment to the assignments. + assignments.push(assignment); + } + } + None => bail!("Missing input tasks for transition {} in inclusion", transition.id()), + } + + // Insert the leaf into the transaction tree. + transaction_tree.append(&[transaction_leaf.to_bits_le()])?; + } + + Ok((assignments, global_state_root)) + } + + pub fn prove_execution_stateless, R: Rng + CryptoRng>( + &self, + proving_key: ProvingKey, + execution: Execution, + assignments: &[InclusionAssignment], + global_state_root: N::StateRoot, + rng: &mut R, + ) -> Result> { + match assignments.is_empty() { + true => { + // Ensure the global state root is not zero. + if *global_state_root == Field::zero() { + bail!("Inclusion expected the global state root in the execution to *not* be zero") + } + + // Ensure the inclusion proof in the execution is 'None'. + if execution.inclusion_proof().is_some() { + bail!("Inclusion expected the inclusion proof in the execution to be 'None'") + } + // Return the execution. + Execution::from(execution.into_transitions(), global_state_root, None) + } + false => { + // Compute the inclusion batch proof. + let (global_state_root, inclusion_proof) = Self::prove_batch::(&proving_key, assignments, rng)?; + // Return the execution. + Execution::from(execution.into_transitions(), global_state_root, Some(inclusion_proof)) + } + } + } + /// Returns a new execution with an inclusion proof, for the given execution. pub fn prove_execution, R: Rng + CryptoRng>( &self, @@ -485,6 +584,73 @@ impl Inclusion { Ok(()) } + /// Checks the inclusion proof for the execution. + /// Note: This does *not* check that the global state root exists in the ledger. + pub fn verify_execution_stateless(execution: &Execution, verifying_key: VerifyingKey) -> Result<()> { + // Retrieve the global state root. + let global_state_root = execution.global_state_root(); + + // Retrieve the inclusion proof. + let inclusion_proof = execution.inclusion_proof(); + + // Initialize an empty transaction tree. + let mut transaction_tree = N::merkle_tree_bhp::(&[])?; + // Initialize a vector for the batch verifier inputs. + let mut batch_verifier_inputs = vec![]; + + // Construct the batch verifier inputs. + for (transition_index, transition) in execution.transitions().enumerate() { + // Retrieve the local state root. + let local_state_root = *transaction_tree.root(); + + // Iterate through the inputs. + for input in transition.inputs() { + // Filter the inputs for records. + if let Input::Record(serial_number, _) = input { + // Add the public inputs to the batch verifier inputs. + batch_verifier_inputs.push(vec![ + N::Field::one(), + **global_state_root, + *local_state_root, + **serial_number, + ]); + } + } + + // Construct the transaction leaf. + let transaction_leaf = TransactionLeaf::new_execution(transition_index as u16, **transition.id()); + // Insert the leaf into the transaction tree. + transaction_tree.append(&[transaction_leaf.to_bits_le()])?; + } + + // If there are no batch verifier inputs, then ensure the inclusion proof is 'None'. + if batch_verifier_inputs.is_empty() && inclusion_proof.is_some() { + bail!("No input records in the execution. Expected the inclusion proof to be 'None'") + } + // If there are batch verifier inputs, then ensure the inclusion proof is 'Some'. + if !batch_verifier_inputs.is_empty() && inclusion_proof.is_none() { + bail!("Missing inclusion proof for the execution") + } + + // Verify the inclusion proof. + if let Some(inclusion_proof) = inclusion_proof { + // Ensure the global state root is not zero. + if *global_state_root == Field::zero() { + bail!("Inclusion expected the global state root in the execution to *not* be zero") + } + + // Fetch the inclusion verifying key. + // let verifying_key = VerifyingKey::::new(N::inclusion_verifying_key().clone()); + // Verify the inclusion proof. + ensure!( + verifying_key.verify_batch(N::INCLUSION_FUNCTION_NAME, &batch_verifier_inputs, inclusion_proof), + "Inclusion proof is invalid" + ); + } + + Ok(()) + } + /// Checks the inclusion proof for the fee. /// Note: This does *not* check that the global state root exists in the ledger. pub fn verify_fee(fee: &Fee) -> Result<()> { diff --git a/utilities/Cargo.toml b/utilities/Cargo.toml index 302df2805a..90b4231a25 100644 --- a/utilities/Cargo.toml +++ b/utilities/Cargo.toml @@ -72,4 +72,5 @@ default-features = false default = [ "aleo-std/cpu", "derive", "num_cpus", "std" ] serial = [ "derive" ] std = [ ] +web = [ ] derive = [ "snarkvm-utilities-derives" ] diff --git a/utilities/src/parallel.rs b/utilities/src/parallel.rs index 8b71efdc84..e550b6089e 100644 --- a/utilities/src/parallel.rs +++ b/utilities/src/parallel.rs @@ -67,18 +67,18 @@ pub fn max_available_threads() -> usize { } #[inline(always)] -#[cfg(not(feature = "serial"))] +#[cfg(all(not(feature = "serial"), not(feature = "web")))] pub fn execute_with_max_available_threads(f: impl FnOnce() -> T + Send) -> T { execute_with_threads(f, max_available_threads()) } #[inline(always)] -#[cfg(feature = "serial")] +#[cfg(any(feature = "serial", feature = "web"))] pub fn execute_with_max_available_threads(f: impl FnOnce() -> T + Send) -> T { f() } -#[cfg(not(feature = "serial"))] +#[cfg(all(not(feature = "serial")))] #[inline(always)] fn execute_with_threads(f: impl FnOnce() -> T + Send, num_threads: usize) -> T { let pool = rayon::ThreadPoolBuilder::new().num_threads(num_threads).build().unwrap(); diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 9ac6b670bb..ba31de604e 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -26,6 +26,11 @@ edition = "2021" [lib] crate-type = [ "cdylib", "rlib" ] +[dependencies.snarkvm-circuit] +path = "../circuit" +version = "0.10.2" +optional = true + [dependencies.snarkvm-console] path = "../console" version = "0.10.2" @@ -76,7 +81,9 @@ version = "0.3.34" [features] default = [ "full" ] -full = [ "console", "curves", "fields", "synthesizer", "utilities" ] +full = [ "circuit", "console", "curves", "fields", "synthesizer", "utilities" ] +serial = ["snarkvm-console/serial", "snarkvm-curves/serial", "snarkvm-fields/serial", "snarkvm-synthesizer/serial", "snarkvm-utilities/serial"] +circuit = [ "snarkvm-circuit" ] console = [ "snarkvm-console" ] curves = [ "snarkvm-curves" ] fields = [ "snarkvm-fields" ] diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 73003d8d50..6f931c597a 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . +#[cfg(feature = "circuit")] +pub use snarkvm_circuit::AleoV0; + #[cfg(feature = "console")] pub use snarkvm_console::*; @@ -23,6 +26,9 @@ pub use snarkvm_curves::{bls12_377::*, edwards_bls12::*}; #[cfg(feature = "fields")] pub use snarkvm_fields::*; +#[cfg(feature = "synthesizer")] +pub use snarkvm_synthesizer::*; + #[cfg(feature = "utilities")] pub use snarkvm_utilities::*;