diff --git a/algorithms/benches/snark/marlin.rs b/algorithms/benches/snark/marlin.rs index 0e7df6ef77..629465df18 100644 --- a/algorithms/benches/snark/marlin.rs +++ b/algorithms/benches/snark/marlin.rs @@ -28,7 +28,6 @@ use snarkvm_utilities::{CanonicalDeserialize, CanonicalSerialize, TestRng}; use criterion::Criterion; -use itertools::Itertools; use std::collections::BTreeMap; type MarlinInst = MarlinSNARK; @@ -74,8 +73,11 @@ fn snark_prove(c: &mut Criterion) { let (circuit, _) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng); - let params = MarlinInst::circuit_setup(&universal_srs, &circuit).unwrap(); - b.iter(|| MarlinInst::prove(&fs_parameters, ¶ms.0, &circuit, rng).unwrap()) + let (pk, _) = MarlinInst::circuit_setup(&universal_srs, &circuit).unwrap(); + let mut keys_to_constraints = BTreeMap::new(); + let constraints = [circuit]; + keys_to_constraints.insert(&pk, &constraints[..]); + b.iter(move || MarlinInst::prove_batch(&fs_parameters, &keys_to_constraints, rng).unwrap()) }); } @@ -110,14 +112,10 @@ fn snark_batch_prove(c: &mut Criterion) { pks.push(pk); all_circuits.push(circuits); } - // We need to create references to the circuits we just created - let all_circuit_refs = (0..circuit_batch_size) - .map(|i| (0..instance_batch_size).map(|j| &all_circuits[i][j]).collect_vec()) - .collect_vec(); - for i in 0..circuit_batch_size { - keys_to_constraints.insert(&pks[i], all_circuit_refs[i].as_slice()); + keys_to_constraints.insert(&pks[i], all_circuits[i].as_slice()); } + b.iter(|| MarlinInst::prove_batch(&fs_parameters, &keys_to_constraints, rng).unwrap()) }); } @@ -137,7 +135,7 @@ fn snark_verify(c: &mut Criterion) { let (pk, vk) = MarlinInst::circuit_setup(&universal_srs, &circuit).unwrap(); - let proof = MarlinInst::prove(&fs_parameters, &pk, &circuit, rng).unwrap(); + let proof = MarlinInst::prove(&fs_parameters, &pk, circuit, rng).unwrap(); b.iter(|| { let verification = MarlinInst::verify(&fs_parameters, &vk, public_inputs.as_slice(), &proof).unwrap(); assert!(verification); @@ -181,13 +179,8 @@ fn snark_batch_verify(c: &mut Criterion) { all_circuits.push(circuits); all_inputs.push(inputs); } - // We need to create references to the circuits and inputs we just created - let all_circuit_refs = (0..circuit_batch_size) - .map(|i| (0..instance_batch_size).map(|j| &all_circuits[i][j]).collect_vec()) - .collect_vec(); - for i in 0..circuit_batch_size { - keys_to_constraints.insert(&pks[i], all_circuit_refs[i].as_slice()); + keys_to_constraints.insert(&pks[i], all_circuits[i].as_slice()); keys_to_inputs.insert(&vks[i], all_inputs[i].as_slice()); } 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 0f285afe00..04384c7957 100644 --- a/algorithms/src/snark/marlin/ahp/prover/round_functions/mod.rs +++ b/algorithms/src/snark/marlin/ahp/prover/round_functions/mod.rs @@ -38,7 +38,7 @@ mod third; impl AHPForR1CS { /// Initialize the AHP prover. pub fn init_prover<'a, C: ConstraintSynthesizer>( - circuits_to_constraints: &BTreeMap<&'a Circuit, &[&C]>, + circuits_to_constraints: &BTreeMap<&'a Circuit, &[C]>, ) -> Result, AHPError> { let init_time = start_timer!(|| "AHP::Prover::Init"); diff --git a/algorithms/src/snark/marlin/marlin.rs b/algorithms/src/snark/marlin/marlin.rs index b76a4066ec..9979df713f 100644 --- a/algorithms/src/snark/marlin/marlin.rs +++ b/algorithms/src/snark/marlin/marlin.rs @@ -369,7 +369,7 @@ where /// https://github.com/AleoHQ/protocol-docs/tree/main/marlin fn prove_batch_with_terminator, R: Rng + CryptoRng>( fs_parameters: &Self::FSParameters, - keys_to_constraints: &BTreeMap<&CircuitProvingKey, &[&C]>, + keys_to_constraints: &BTreeMap<&CircuitProvingKey, &[C]>, terminator: &AtomicBool, zk_rng: &mut R, ) -> Result { @@ -966,7 +966,7 @@ pub mod test { // Test native proof and verification. let fs_parameters = FS::sample_parameters(); - let proof = TestSNARK::prove(&fs_parameters, &pk, &circ, &mut rng).unwrap(); + let proof = TestSNARK::prove(&fs_parameters, &pk, circ, &mut rng).unwrap(); assert!( TestSNARK::verify(&fs_parameters, &vk.clone(), public_inputs.as_slice(), &proof).unwrap(), diff --git a/algorithms/src/snark/marlin/tests.rs b/algorithms/src/snark/marlin/tests.rs index b80b62de8d..b746ebdb83 100644 --- a/algorithms/src/snark/marlin/tests.rs +++ b/algorithms/src/snark/marlin/tests.rs @@ -54,7 +54,7 @@ mod marlin { let certificate = $marlin_inst::prove_vk(&fs_parameters, &index_vk, &index_pk).unwrap(); assert!($marlin_inst::verify_vk(&fs_parameters, &circ, &index_vk, &certificate).unwrap()); - let proof = $marlin_inst::prove(&fs_parameters, &index_pk, &circ, rng).unwrap(); + let proof = $marlin_inst::prove(&fs_parameters, &index_pk, circ, rng).unwrap(); println!("Called prover"); assert!($marlin_inst::verify(&fs_parameters, &index_vk, public_inputs, &proof).unwrap()); @@ -88,20 +88,12 @@ mod marlin { let mut pks_to_constraints = BTreeMap::new(); let mut vks_to_inputs = BTreeMap::new(); - let mut constraint_refs = Vec::with_capacity(index_keys.len()); for (index_pk, index_vk) in index_keys.iter() { let circuit_constraints = &constraints[&index_pk.circuit.id]; - let mut circuit_constraint_refs = Vec::with_capacity(circuit_constraints.len()); - for constraint in circuit_constraints.iter() { - circuit_constraint_refs.push(constraint) - } - constraint_refs.push(circuit_constraint_refs); + pks_to_constraints.insert(index_pk, circuit_constraints.as_slice()); let circuit_inputs = &inputs[&index_pk.circuit.id]; vks_to_inputs.insert(index_vk, circuit_inputs.as_slice()); } - for (i, (index_pk, _)) in index_keys.iter().enumerate() { - pks_to_constraints.insert(index_pk, constraint_refs[i].as_slice()); - } let proof = $marlin_inst::prove_batch(&fs_parameters, &pks_to_constraints, rng).unwrap(); @@ -304,7 +296,7 @@ mod marlin_hiding { let (index_pk, index_vk) = MarlinInst::circuit_setup(&universal_srs, &circuit).unwrap(); println!("Called circuit setup"); - let proof = MarlinInst::prove(&fs_parameters, &index_pk, &circuit, rng).unwrap(); + let proof = MarlinInst::prove(&fs_parameters, &index_pk, circuit, rng).unwrap(); println!("Called prover"); assert!(MarlinInst::verify(&fs_parameters, &index_vk, public_inputs, &proof).unwrap()); @@ -434,13 +426,12 @@ mod marlin_hiding { let fs_parameters = FS::sample_parameters(); let (index_pk, index_vk) = MarlinInst::circuit_setup(&universal_srs, &circuit).unwrap(); - println!("Called circuit setup"); - - let proof = MarlinInst::prove(&fs_parameters, &index_pk, &circuit, rng).unwrap(); - println!("Called prover"); universal_srs.download_powers_for(0..2usize.pow(18)).unwrap(); let (new_pk, new_vk) = MarlinInst::circuit_setup(&universal_srs, &circuit).unwrap(); + + let proof = MarlinInst::prove(&fs_parameters, &index_pk, circuit, rng).unwrap(); + assert_eq!(index_pk, new_pk); assert_eq!(index_vk, new_vk); assert!(MarlinInst::verify(&fs_parameters, &index_vk, public_inputs.clone(), &proof).unwrap()); @@ -461,10 +452,8 @@ mod marlin_hiding { let num_variables = 2usize.pow(15) - 10; let (circuit1, public_inputs1) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng); let (pk1, vk1) = MarlinInst::circuit_setup(&universal_srs, &circuit1).unwrap(); - println!("Called circuit setup"); - let proof1 = MarlinInst::prove(&fs_parameters, &pk1, &circuit1, rng).unwrap(); - println!("Called prover"); + let proof1 = MarlinInst::prove(&fs_parameters, &pk1, circuit1, rng).unwrap(); assert!(MarlinInst::verify(&fs_parameters, &vk1, public_inputs1.clone(), &proof1).unwrap()); /*****************************************************************************/ @@ -475,10 +464,9 @@ mod marlin_hiding { let num_variables = 2usize.pow(19) - 10; let (circuit2, public_inputs2) = TestCircuit::gen_rand(mul_depth, num_constraints, num_variables, rng); let (pk2, vk2) = MarlinInst::circuit_setup(&universal_srs, &circuit2).unwrap(); - println!("Called circuit setup"); - let proof2 = MarlinInst::prove(&fs_parameters, &pk2, &circuit2, rng).unwrap(); - println!("Called prover"); + let proof2 = MarlinInst::prove(&fs_parameters, &pk2, circuit2, rng).unwrap(); + assert!(MarlinInst::verify(&fs_parameters, &vk2, public_inputs2, &proof2).unwrap()); /*****************************************************************************/ assert!(MarlinInst::verify(&fs_parameters, &vk1, public_inputs1, &proof1).unwrap()); diff --git a/algorithms/src/traits/snark.rs b/algorithms/src/traits/snark.rs index 0316b753ef..9686bf8406 100644 --- a/algorithms/src/traits/snark.rs +++ b/algorithms/src/traits/snark.rs @@ -95,7 +95,7 @@ pub trait SNARK { fn prove_batch, R: Rng + CryptoRng>( fs_parameters: &Self::FSParameters, - keys_to_constraints: &BTreeMap<&Self::ProvingKey, &[&C]>, + keys_to_constraints: &BTreeMap<&Self::ProvingKey, &[C]>, rng: &mut R, ) -> Result { Self::prove_batch_with_terminator(fs_parameters, keys_to_constraints, &AtomicBool::new(false), rng) @@ -104,7 +104,7 @@ pub trait SNARK { fn prove, R: Rng + CryptoRng>( fs_parameters: &Self::FSParameters, proving_key: &Self::ProvingKey, - constraints: &C, + constraints: C, rng: &mut R, ) -> Result { let mut keys_to_constraints = BTreeMap::new(); @@ -115,24 +115,11 @@ pub trait SNARK { fn prove_batch_with_terminator, R: Rng + CryptoRng>( fs_parameters: &Self::FSParameters, - keys_to_constraints: &BTreeMap<&Self::ProvingKey, &[&C]>, + keys_to_constraints: &BTreeMap<&Self::ProvingKey, &[C]>, terminator: &AtomicBool, rng: &mut R, ) -> Result; - fn prove_with_terminator, R: Rng + CryptoRng>( - fs_parameters: &Self::FSParameters, - proving_key: &Self::ProvingKey, - constraints: &C, - terminator: &AtomicBool, - rng: &mut R, - ) -> Result { - let mut keys_to_constraints = BTreeMap::new(); - let constraints = [constraints]; - keys_to_constraints.insert(proving_key, &constraints[..]); - Self::prove_batch_with_terminator(fs_parameters, &keys_to_constraints, terminator, rng) - } - fn verify_vk>( fs_parameters: &Self::FSParameters, circuit: &C, diff --git a/circuit/environment/src/helpers/assignment.rs b/circuit/environment/src/helpers/assignment.rs index 1e28d98f21..56e0b8c08f 100644 --- a/circuit/environment/src/helpers/assignment.rs +++ b/circuit/environment/src/helpers/assignment.rs @@ -299,7 +299,7 @@ mod tests { let (index_pk, index_vk) = MarlinInst::circuit_setup(&universal_srs, &assignment).unwrap(); println!("Called circuit setup"); - let proof = MarlinInst::prove(&fs_pp, &index_pk, &assignment, rng).unwrap(); + let proof = MarlinInst::prove(&fs_pp, &index_pk, assignment, rng).unwrap(); println!("Called prover"); let one = ::BaseField::one(); diff --git a/circuit/environment/src/helpers/converter.rs b/circuit/environment/src/helpers/converter.rs index 14788902e6..4fcf463ec2 100644 --- a/circuit/environment/src/helpers/converter.rs +++ b/circuit/environment/src/helpers/converter.rs @@ -234,7 +234,7 @@ mod tests { let (index_pk, index_vk) = MarlinInst::circuit_setup(&universal_srs, &Circuit).unwrap(); println!("Called circuit setup"); - let proof = MarlinInst::prove(&fs_pp, &index_pk, &Circuit, rng).unwrap(); + let proof = MarlinInst::prove(&fs_pp, &index_pk, Circuit, rng).unwrap(); println!("Called prover"); assert!(MarlinInst::verify(&fs_pp, &index_vk, [*one, *one], &proof).unwrap()); diff --git a/console/program/src/data/identifier/mod.rs b/console/program/src/data/identifier/mod.rs index ddb0e440f9..18431171fe 100644 --- a/console/program/src/data/identifier/mod.rs +++ b/console/program/src/data/identifier/mod.rs @@ -73,6 +73,20 @@ impl TryFrom<&str> for Identifier { } } +impl Ord for Identifier { + /// Ordering is determined by the field element. + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for Identifier { + /// Ordering is determined by the field element. + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; diff --git a/console/program/src/id/mod.rs b/console/program/src/id/mod.rs index ae67ff797b..4b8e23ea65 100644 --- a/console/program/src/id/mod.rs +++ b/console/program/src/id/mod.rs @@ -106,8 +106,8 @@ impl Ord for ProgramID { /// Ordering is determined by the network first, then the program name second. fn cmp(&self, other: &Self) -> Ordering { match self.network == other.network { - true => self.name.to_string().cmp(&other.name.to_string()), - false => self.network.to_string().cmp(&other.network.to_string()), + true => self.name.cmp(&other.name), + false => self.network.cmp(&other.network), } } } @@ -135,23 +135,23 @@ mod tests { let import4 = ProgramID::::from_str("foo.aleo")?; assert_eq!(import1.partial_cmp(&import1), Some(Ordering::Equal)); - assert_eq!(import1.partial_cmp(&import2), Some(Ordering::Less)); + assert_eq!(import1.partial_cmp(&import2), Some(Ordering::Greater)); assert_eq!(import1.partial_cmp(&import3), Some(Ordering::Equal)); - assert_eq!(import1.partial_cmp(&import4), Some(Ordering::Less)); + assert_eq!(import1.partial_cmp(&import4), Some(Ordering::Greater)); - assert_eq!(import2.partial_cmp(&import1), Some(Ordering::Greater)); + assert_eq!(import2.partial_cmp(&import1), Some(Ordering::Less)); assert_eq!(import2.partial_cmp(&import2), Some(Ordering::Equal)); - assert_eq!(import2.partial_cmp(&import3), Some(Ordering::Greater)); + assert_eq!(import2.partial_cmp(&import3), Some(Ordering::Less)); assert_eq!(import2.partial_cmp(&import4), Some(Ordering::Equal)); assert_eq!(import3.partial_cmp(&import1), Some(Ordering::Equal)); - assert_eq!(import3.partial_cmp(&import2), Some(Ordering::Less)); + assert_eq!(import3.partial_cmp(&import2), Some(Ordering::Greater)); assert_eq!(import3.partial_cmp(&import3), Some(Ordering::Equal)); - assert_eq!(import3.partial_cmp(&import4), Some(Ordering::Less)); + assert_eq!(import3.partial_cmp(&import4), Some(Ordering::Greater)); - assert_eq!(import4.partial_cmp(&import1), Some(Ordering::Greater)); + assert_eq!(import4.partial_cmp(&import1), Some(Ordering::Less)); assert_eq!(import4.partial_cmp(&import2), Some(Ordering::Equal)); - assert_eq!(import4.partial_cmp(&import3), Some(Ordering::Greater)); + assert_eq!(import4.partial_cmp(&import3), Some(Ordering::Less)); assert_eq!(import4.partial_cmp(&import4), Some(Ordering::Equal)); Ok(()) diff --git a/parameters/examples/inclusion.rs b/parameters/examples/inclusion.rs index 4c14ee17f7..3e014f42cc 100644 --- a/parameters/examples/inclusion.rs +++ b/parameters/examples/inclusion.rs @@ -125,7 +125,7 @@ pub fn inclusion>() -> Result<()> { let (proving_key, verifying_key) = universal_srs.to_circuit_key(inclusion_function_name, &assignment)?; // Ensure the proving key and verifying keys are valid. - let proof = proving_key.prove(inclusion_function_name, &assignment, &mut thread_rng())?; + let proof = proving_key.prove(inclusion_function_name, assignment, &mut thread_rng())?; assert!(verifying_key.verify( inclusion_function_name, &[N::Field::one(), **state_path.global_state_root(), *Field::::zero(), *serial_number], diff --git a/synthesizer/coinbase/src/helpers/puzzle_commitment/mod.rs b/synthesizer/coinbase/src/helpers/puzzle_commitment/mod.rs index fd1738fef7..22e4b7c96a 100644 --- a/synthesizer/coinbase/src/helpers/puzzle_commitment/mod.rs +++ b/synthesizer/coinbase/src/helpers/puzzle_commitment/mod.rs @@ -24,7 +24,7 @@ use super::*; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct PuzzleCommitment { /// The commitment for the solution. - commitment: KZGCommitment<::PairingCurve>, + commitment: KZGCommitment, } impl PuzzleCommitment { diff --git a/synthesizer/snark/src/batch/bytes.rs b/synthesizer/snark/src/batch/bytes.rs new file mode 100644 index 0000000000..d530bc7fd2 --- /dev/null +++ b/synthesizer/snark/src/batch/bytes.rs @@ -0,0 +1,88 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the snarkVM library. + +// The snarkVM library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The snarkVM library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the snarkVM library. If not, see . + +use super::*; + +const PROVING: u8 = 0; +const VERIFYING: u8 = 1; +const BATCH_VERSION_0: u8 = 0; + +impl FromBytes for KeyBatch { + /// Reads the proving key batch from a buffer. + fn read_le(mut reader: R) -> IoResult { + // Read the version. + let version = u8::read_le(&mut reader)?; + // Ensure the version is valid. + if version != BATCH_VERSION_0 { + return Err(error("Invalid key version")); + } + // Read the mode. + let mode = u8::read_le(&mut reader)?; + let mode = match mode { + PROVING => KeyMode::Proving, + VERIFYING => KeyMode::Verifying, + _ => return Err(error("Invalid key mode")), + }; + // Read the number of keys. + let batch_len = usize::try_from(u32::read_le(&mut reader)?); + let batch_len = if let Ok(batch_len) = batch_len { + batch_len + } else { + return Err(error("Could not convert u32 to usize")); + }; + let mut batch = Vec::with_capacity(batch_len); + // Read the keys + for _ in 0..batch_len { + match mode { + KeyMode::Proving => { + batch.push(Key::ProvingKey(ProvingKey::new(Arc::new(FromBytes::read_le(&mut reader)?)))); + } + KeyMode::Verifying => { + batch.push(Key::VerifyingKey(VerifyingKey::new(Arc::new(FromBytes::read_le(&mut reader)?)))); + } + } + } + Ok(Self { batch, mode }) + } +} + +impl ToBytes for KeyBatch { + /// Writes the proving key to a buffer. + fn write_le(&self, mut writer: W) -> IoResult<()> { + // Write the version. + BATCH_VERSION_0.write_le(&mut writer)?; + // Write the key types + match self.mode { + KeyMode::Proving => PROVING.write_le(&mut writer)?, + KeyMode::Verifying => VERIFYING.write_le(&mut writer)?, + } + // Write the number of Keys. + let batch_len = u32::try_from(self.batch.len()); + if batch_len.is_err() { + return Err(error("Could not convert usize to u32")); + } else { + batch_len.unwrap().write_le(&mut writer)?; + }; + // Write the bytes. + for key in &self.batch { + match key { + Key::VerifyingKey(vk) => vk.write_le(&mut writer)?, + Key::ProvingKey(pk) => pk.write_le(&mut writer)?, + } + } + Ok(()) + } +} diff --git a/synthesizer/snark/src/batch/mod.rs b/synthesizer/snark/src/batch/mod.rs new file mode 100644 index 0000000000..fee0863e25 --- /dev/null +++ b/synthesizer/snark/src/batch/mod.rs @@ -0,0 +1,142 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the snarkVM library. + +// The snarkVM library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The snarkVM library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the snarkVM library. If not, see . + +use super::*; + +mod bytes; +mod parse; + +use circuit::Assignment; +use console::program::Identifier; +use std::collections::BTreeMap; + +#[derive(Clone, PartialEq, Eq)] +pub enum Key { + ProvingKey(ProvingKey), + VerifyingKey(VerifyingKey), +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum KeyMode { + Proving = 0, + Verifying = 1, +} + +#[derive(Clone)] +pub struct KeyBatch { + /// A batch of proving or verifying keys + batch: Vec>, + mode: KeyMode, +} + +impl KeyBatch { + /// Initializes a new batch. + pub fn new(keys: usize, mode: KeyMode) -> Self { + Self { batch: Vec::with_capacity(keys), mode } + } + + /// Adds keys to the batch. + pub fn add(&mut self, key: Key) -> Result<()> { + match key { + Key::VerifyingKey(_) => { + if matches!(self.mode, KeyMode::Proving) { + bail!("Cannot add a verifying key to a proving batch") + } + } + Key::ProvingKey(_) => { + if matches!(self.mode, KeyMode::Verifying) { + bail!("Cannot add a proving key to a verifying batch") + } + } + } + self.batch.push(key); + Ok(()) + } + + /// Returns a proof for the given batch of assignments on the batch of circuits. + pub fn prove( + &self, + function_names: &[&Identifier], + assignments: &[&[Assignment]], + proves_inclusion: bool, + rng: &mut R, + ) -> Result> { + #[cfg(feature = "aleo-cli")] + let timer = std::time::Instant::now(); + + let mut keys_to_constraints = BTreeMap::new(); + for (key, &circuit_assignments) in self.batch.iter().zip(assignments) { + match key { + Key::VerifyingKey(_) => { + bail!("Cannot use a verifying key to prove a batch") + } + Key::ProvingKey(pk) => { + keys_to_constraints.insert(pk.deref(), circuit_assignments); + } + } + } + + // Compute the batch proof. + let batch_proof = Proof::new( + Marlin::::prove_batch(N::marlin_fs_parameters(), &keys_to_constraints, rng)?, + proves_inclusion, + ); + + #[cfg(feature = "aleo-cli")] + println!("{}", format!(" • Executed '{:?}' (in {} ms)", function_names, timer.elapsed().as_millis()).dimmed()); + Ok(batch_proof) + } + + /// Returns `true` if the batch proof is valid for the given public inputs. + pub fn verify( + &self, + function_names: &[&Identifier], + inputs: &[Vec>], + proof: &Proof, + ) -> Result<()> { + #[cfg(feature = "aleo-cli")] + let timer = std::time::Instant::now(); + + // Verify the batch proof. + let mut keys_to_inputs = BTreeMap::new(); + for (key, circuit_inputs) in self.batch.iter().zip(inputs) { + match key { + Key::VerifyingKey(vk) => { + keys_to_inputs.insert(vk.deref(), circuit_inputs.as_slice()); + } + Key::ProvingKey(_) => { + bail!("Cannot use a proving key to verify a batch") + } + } + } + match Marlin::::verify_batch(N::marlin_fs_parameters(), &keys_to_inputs, proof) { + Ok(_is_valid) => { + #[cfg(feature = "aleo-cli")] + { + let elapsed = timer.elapsed().as_millis(); + println!("{}", format!(" • Verified '{:?}' (in {elapsed} ms)", function_names).dimmed()); + } + + Ok(()) + } + Err(error) => { + #[cfg(feature = "aleo-cli")] + println!("{}", format!(" • Verifier failed: {error}").dimmed()); + Err(error.into()) + } + } + } +} diff --git a/synthesizer/snark/src/batch/parse.rs b/synthesizer/snark/src/batch/parse.rs new file mode 100644 index 0000000000..d217b30251 --- /dev/null +++ b/synthesizer/snark/src/batch/parse.rs @@ -0,0 +1,37 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the snarkVM library. + +// The snarkVM library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The snarkVM library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the snarkVM library. If not, see . + +use super::*; + +static TAG: &str = "batch"; + +impl Debug for KeyBatch { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} + +impl Display for KeyBatch { + /// Writes the batch as a bech32m string. + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + // Convert the batch to bytes. + let bytes = self.to_bytes_le().map_err(|_| fmt::Error)?; + // Encode the bytes into bech32m. + let string = bech32::encode(TAG, bytes.to_base32(), bech32::Variant::Bech32m).map_err(|_| fmt::Error)?; + // Output the string. + Display::fmt(&string, f) + } +} diff --git a/synthesizer/snark/src/lib.rs b/synthesizer/snark/src/lib.rs index f4399d3819..ed2fbcd6de 100644 --- a/synthesizer/snark/src/lib.rs +++ b/synthesizer/snark/src/lib.rs @@ -30,6 +30,9 @@ use colored::Colorize; type Marlin = marlin::MarlinSNARK<::PairingCurve, FiatShamir, marlin::MarlinHidingMode>; +mod batch; +pub use batch::{Key, KeyBatch, KeyMode}; + mod certificate; pub use certificate::Certificate; @@ -115,7 +118,7 @@ pub(crate) mod test_helpers { .get_or_init(|| { let assignment = sample_assignment(); let (proving_key, _) = sample_keys(); - proving_key.prove("test", &assignment, &mut TestRng::default()).unwrap() + proving_key.prove("test", assignment, &mut TestRng::default()).unwrap() }) .clone() } @@ -150,7 +153,7 @@ mod test { let (proving_key, verifying_key) = srs.to_circuit_key("test", &assignment).unwrap(); println!("Called circuit setup"); - let proof = proving_key.prove("test", &assignment, &mut TestRng::default()).unwrap(); + let proof = proving_key.prove("test", assignment, &mut TestRng::default()).unwrap(); println!("Called prover"); let one = ::BaseField::one(); diff --git a/synthesizer/snark/src/proof/bytes.rs b/synthesizer/snark/src/proof/bytes.rs index 0f541366c0..e1c05faba9 100644 --- a/synthesizer/snark/src/proof/bytes.rs +++ b/synthesizer/snark/src/proof/bytes.rs @@ -16,19 +16,23 @@ use super::*; +const PROOF_VERSION_0: u8 = 0; + impl FromBytes for Proof { /// Reads the proof from a buffer. fn read_le(mut reader: R) -> IoResult { // Read the version. let version = u8::read_le(&mut reader)?; // Ensure the version is valid. - if version != 0 { + if version != PROOF_VERSION_0 { return Err(error("Invalid proof version")); } // Read the proof. + let proves_inclusion = bool::read_le(&mut reader)?; + // Read the proof. let proof = FromBytes::read_le(&mut reader)?; // Return the proof. - Ok(Self { proof }) + Ok(Self { proof, proves_inclusion }) } } @@ -36,7 +40,9 @@ impl ToBytes for Proof { /// Writes the proof to a buffer. fn write_le(&self, mut writer: W) -> IoResult<()> { // Write the version. - 0u8.write_le(&mut writer)?; + PROOF_VERSION_0.write_le(&mut writer)?; + // Write whether we prove inclusion + self.proves_inclusion.write_le(&mut writer)?; // Write the bytes. self.proof.write_le(&mut writer) } diff --git a/synthesizer/snark/src/proof/mod.rs b/synthesizer/snark/src/proof/mod.rs index d21ea6a0df..0aeeaa3669 100644 --- a/synthesizer/snark/src/proof/mod.rs +++ b/synthesizer/snark/src/proof/mod.rs @@ -24,12 +24,19 @@ mod serialize; pub struct Proof { /// The proof. proof: marlin::Proof, + /// Does the proof prove inclusion? + proves_inclusion: bool, } impl Proof { /// Initializes a new proof. - pub(super) const fn new(proof: marlin::Proof) -> Self { - Self { proof } + pub(super) const fn new(proof: marlin::Proof, proves_inclusion: bool) -> Self { + Self { proof, proves_inclusion } + } + + /// Returns whether we prove inclusion. + pub const fn proves_inclusion(&self) -> bool { + self.proves_inclusion } } diff --git a/synthesizer/snark/src/proving_key/bytes.rs b/synthesizer/snark/src/proving_key/bytes.rs index bd0d6dfd7d..bb0cb173d5 100644 --- a/synthesizer/snark/src/proving_key/bytes.rs +++ b/synthesizer/snark/src/proving_key/bytes.rs @@ -16,13 +16,15 @@ use super::*; +const PROVINGKEY_VERSION_0: u8 = 0; + impl FromBytes for ProvingKey { /// Reads the proving key from a buffer. fn read_le(mut reader: R) -> IoResult { // Read the version. let version = u8::read_le(&mut reader)?; // Ensure the version is valid. - if version != 0 { + if version != PROVINGKEY_VERSION_0 { return Err(error("Invalid proving key version")); } // Read the proving key. @@ -36,7 +38,7 @@ impl ToBytes for ProvingKey { /// Writes the proving key to a buffer. fn write_le(&self, mut writer: W) -> IoResult<()> { // Write the version. - 0u8.write_le(&mut writer)?; + PROVINGKEY_VERSION_0.write_le(&mut writer)?; // Write the bytes. self.proving_key.write_le(&mut writer) } diff --git a/synthesizer/snark/src/proving_key/mod.rs b/synthesizer/snark/src/proving_key/mod.rs index b63208ffaf..364c5d4716 100644 --- a/synthesizer/snark/src/proving_key/mod.rs +++ b/synthesizer/snark/src/proving_key/mod.rs @@ -20,9 +20,7 @@ mod bytes; mod parse; mod serialize; -use std::collections::BTreeMap; - -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct ProvingKey { /// The proving key for the function. proving_key: Arc>, @@ -38,43 +36,20 @@ impl ProvingKey { pub fn prove( &self, function_name: &str, - assignment: &circuit::Assignment, + assignment: circuit::Assignment, rng: &mut R, ) -> Result> { #[cfg(feature = "aleo-cli")] let timer = std::time::Instant::now(); // Compute the proof. - let proof = Proof::new(Marlin::::prove(N::marlin_fs_parameters(), self, assignment, rng)?); + const PROVES_INCLUSION: bool = false; + let proof = Proof::new(Marlin::::prove(N::marlin_fs_parameters(), self, assignment, rng)?, PROVES_INCLUSION); #[cfg(feature = "aleo-cli")] println!("{}", format!(" • Executed '{function_name}' (in {} ms)", timer.elapsed().as_millis()).dimmed()); Ok(proof) } - - /// Returns a proof for the given batch of assignments on the circuit. - pub fn prove_batch( - &self, - function_name: &str, - assignments: &[circuit::Assignment], - rng: &mut R, - ) -> Result> { - #[cfg(feature = "aleo-cli")] - let timer = std::time::Instant::now(); - - // Compute the batch proof. - let mut assignment_refs: Vec<&circuit::Assignment> = vec![]; - for assignment in assignments { - assignment_refs.push(assignment); - } - let mut keys_to_constraints = BTreeMap::new(); - keys_to_constraints.insert(self.deref(), assignment_refs.as_slice()); - let batch_proof = Proof::new(Marlin::::prove_batch(N::marlin_fs_parameters(), &keys_to_constraints, rng)?); - - #[cfg(feature = "aleo-cli")] - println!("{}", format!(" • Executed '{function_name}' (in {} ms)", timer.elapsed().as_millis()).dimmed()); - Ok(batch_proof) - } } impl Deref for ProvingKey { diff --git a/synthesizer/snark/src/verifying_key/bytes.rs b/synthesizer/snark/src/verifying_key/bytes.rs index d627374133..602a9f8de3 100644 --- a/synthesizer/snark/src/verifying_key/bytes.rs +++ b/synthesizer/snark/src/verifying_key/bytes.rs @@ -16,13 +16,15 @@ use super::*; +const VERIFYINGKEY_VERSION_0: u8 = 0; + impl FromBytes for VerifyingKey { /// Reads the verifying key from a buffer. fn read_le(mut reader: R) -> IoResult { // Read the version. let version = u8::read_le(&mut reader)?; // Ensure the version is valid. - if version != 0 { + if version != VERIFYINGKEY_VERSION_0 { return Err(error("Invalid verifying key version")); } // Read the verifying key. @@ -36,7 +38,7 @@ impl ToBytes for VerifyingKey { /// Writes the verifying key to a buffer. fn write_le(&self, mut writer: W) -> IoResult<()> { // Write the version. - 0u8.write_le(&mut writer)?; + VERIFYINGKEY_VERSION_0.write_le(&mut writer)?; // Write the bytes. self.verifying_key.write_le(&mut writer) } diff --git a/synthesizer/snark/src/verifying_key/mod.rs b/synthesizer/snark/src/verifying_key/mod.rs index 36d5a086c2..e38f0c1cc9 100644 --- a/synthesizer/snark/src/verifying_key/mod.rs +++ b/synthesizer/snark/src/verifying_key/mod.rs @@ -20,8 +20,6 @@ mod bytes; mod parse; mod serialize; -use std::collections::BTreeMap; - #[derive(Clone, PartialEq, Eq)] pub struct VerifyingKey { /// The verifying key for the function. @@ -59,32 +57,6 @@ impl VerifyingKey { } } } - - /// Returns `true` if the batch proof is valid for the given public inputs. - pub fn verify_batch(&self, function_name: &str, inputs: &[Vec], proof: &Proof) -> bool { - #[cfg(feature = "aleo-cli")] - let timer = std::time::Instant::now(); - - // Verify the batch proof. - let mut keys_to_inputs = BTreeMap::new(); - keys_to_inputs.insert(self.deref(), inputs); - match Marlin::::verify_batch(N::marlin_fs_parameters(), &keys_to_inputs, proof) { - Ok(is_valid) => { - #[cfg(feature = "aleo-cli")] - { - let elapsed = timer.elapsed().as_millis(); - println!("{}", format!(" • Verified '{function_name}' (in {elapsed} ms)").dimmed()); - } - - is_valid - } - Err(error) => { - #[cfg(feature = "aleo-cli")] - println!("{}", format!(" • Verifier failed: {error}").dimmed()); - false - } - } - } } impl Deref for VerifyingKey { diff --git a/synthesizer/src/block/transaction/execution/bytes.rs b/synthesizer/src/block/transaction/execution/bytes.rs index 85aea16253..d9e5ac31a0 100644 --- a/synthesizer/src/block/transaction/execution/bytes.rs +++ b/synthesizer/src/block/transaction/execution/bytes.rs @@ -37,16 +37,16 @@ impl FromBytes for Execution { (0..num_transitions).map(|_| Transition::read_le(&mut reader)).collect::>>()?; // Read the global state root. let global_state_root = N::StateRoot::read_le(&mut reader)?; - // Read the inclusion proof variant. - let inclusion_variant = u8::read_le(&mut reader)?; - // Read the inclusion proof. - let inclusion_proof = match inclusion_variant { + // Read the proof variant. + let proof_variant = u8::read_le(&mut reader)?; + // Read the proof. + let proof = match proof_variant { 0 => None, 1 => Some(Proof::read_le(&mut reader)?), - _ => return Err(error("Invalid inclusion proof variant '{inclusion_variant}'")), + _ => return Err(error("Invalid proof variant '{proof_variant}'")), }; // Return the new `Execution` instance. - Self::from(transitions.into_iter(), global_state_root, inclusion_proof).map_err(|e| error(e.to_string())) + Self::from(transitions.into_iter(), global_state_root, proof).map_err(|e| error(e.to_string())) } } @@ -63,14 +63,15 @@ impl ToBytes for Execution { } // Write the global state root. self.global_state_root.write_le(&mut writer)?; - // Write the inclusion proof. - match self.inclusion_proof { + + match self.proof { None => 0u8.write_le(&mut writer)?, Some(ref proof) => { 1u8.write_le(&mut writer)?; proof.write_le(&mut writer)?; } } + Ok(()) } } diff --git a/synthesizer/src/block/transaction/execution/mod.rs b/synthesizer/src/block/transaction/execution/mod.rs index 33b230cdad..64a79e0a09 100644 --- a/synthesizer/src/block/transaction/execution/mod.rs +++ b/synthesizer/src/block/transaction/execution/mod.rs @@ -18,36 +18,40 @@ mod bytes; mod serialize; mod string; -use crate::{snark::Proof, Transition}; -use console::{account::Field, network::prelude::*}; - +use crate::{ + snark::{KeyBatch, Proof}, + Transition, +}; +use circuit::Assignment; +use console::{account::Field, network::prelude::*, program::Identifier}; use indexmap::IndexMap; +// type ProvesInclusion = bool; + #[derive(Clone, Default, PartialEq, Eq)] pub struct Execution { /// The transitions. transitions: IndexMap>, /// The global state root. global_state_root: N::StateRoot, - /// The inclusion proof. - inclusion_proof: Option>, + /// The batch proof of transitions and optionally inclusion + proof: Option>, } impl Execution { /// Initialize a new `Execution` instance. pub fn new() -> Self { - Self { transitions: Default::default(), global_state_root: Default::default(), inclusion_proof: None } + Self { transitions: Default::default(), global_state_root: Default::default(), proof: None } } /// Initializes a new `Execution` instance with the given transitions. pub fn from( transitions: impl Iterator>, global_state_root: N::StateRoot, - inclusion_proof: Option>, + proof: Option>, ) -> Result { // Construct the execution. - let execution = - Self { transitions: transitions.map(|t| (*t.id(), t)).collect(), global_state_root, inclusion_proof }; + let execution = Self { transitions: transitions.map(|t| (*t.id(), t)).collect(), global_state_root, proof }; // Ensure the transitions are not empty. ensure!(!execution.transitions.is_empty(), "Execution cannot initialize from empty list of transitions"); // Return the new `Execution` instance. @@ -60,13 +64,54 @@ impl Execution { } /// Returns the global state root. - pub const fn global_state_root(&self) -> N::StateRoot { + pub fn global_state_root(&self) -> N::StateRoot { self.global_state_root } - /// Returns the inclusion proof. - pub const fn inclusion_proof(&self) -> Option<&Proof> { - self.inclusion_proof.as_ref() + /// Returns the proof. + pub const fn proof(&self) -> Option<&Proof> { + self.proof.as_ref() + } + + /// Returns whether we prove inclusion. + pub const fn proves_inclusion(&self) -> bool { + match self.proof.as_ref() { + Some(proof) => proof.proves_inclusion(), + None => false, + } + } + + /// Proves the execution. + pub fn prove( + &mut self, + batch: KeyBatch, + assignments: &[&[Assignment]], + function_names: &[&Identifier], + rng: &mut R, + ) -> Result<()> { + ensure!(self.proof.is_none(), "Proof already exists!"); + ensure!(assignments.len() == function_names.len(), "Need to have equal assignments and names"); + ensure!(!assignments.is_empty(), "Number of assignments must be greater than zero!"); + + let inclusion_name = Identifier::::from_str(N::INCLUSION_FUNCTION_NAME)?; + let proves_inclusion = **function_names.last().unwrap() == inclusion_name; + let proof = batch.prove(function_names, assignments, proves_inclusion, rng)?; + + self.proof = Some(proof); + + Ok(()) + } + + /// Verifies the execution. + pub fn verify(&self, batch: KeyBatch, inputs: &[Vec>]) -> Result { + ensure!(self.proof.is_some(), "proof missing!"); + + let mut function_names = self.transitions.values().map(|t| t.function_name()).collect::>(); + let inclusion_name = Identifier::::from_str(N::INCLUSION_FUNCTION_NAME)?; + function_names.push(&inclusion_name); + batch.verify(function_names.as_slice(), inputs, self.proof.as_ref().unwrap())?; + + Ok(true) } } @@ -133,4 +178,9 @@ impl Execution { pub fn commitments(&self) -> impl '_ + Iterator> { self.transitions.values().flat_map(Transition::commitments) } + + /// Inserts the global_state_root. + pub fn update_global_state_root(&mut self, global_state_root: N::StateRoot) { + self.global_state_root = global_state_root; + } } diff --git a/synthesizer/src/block/transaction/execution/serialize.rs b/synthesizer/src/block/transaction/execution/serialize.rs index a745c49666..c8aa444c00 100644 --- a/synthesizer/src/block/transaction/execution/serialize.rs +++ b/synthesizer/src/block/transaction/execution/serialize.rs @@ -23,12 +23,12 @@ impl Serialize for Execution { fn serialize(&self, serializer: S) -> Result { match serializer.is_human_readable() { true => { - let mut execution = serializer.serialize_struct("Execution", 3)?; + let mut execution = serializer.serialize_struct("Execution", 4)?; execution .serialize_field("transitions", &self.transitions.values().collect::>>())?; execution.serialize_field("global_state_root", &self.global_state_root)?; - if let Some(inclusion_proof) = &self.inclusion_proof { - execution.serialize_field("inclusion", inclusion_proof)?; + if let Some(proof) = &self.proof { + execution.serialize_field("proof", proof)?; } execution.end() } @@ -48,13 +48,12 @@ impl<'de, N: Network> Deserialize<'de> for Execution { let transitions: Vec<_> = DeserializeExt::take_from_value::(&mut execution, "transitions")?; // Retrieve the global state root. let global_state_root = DeserializeExt::take_from_value::(&mut execution, "global_state_root")?; - // Retrieve the inclusion proof. - let inclusion_proof = serde_json::from_value( - execution.get_mut("inclusion").unwrap_or(&mut serde_json::Value::Null).take(), - ) - .map_err(de::Error::custom)?; + // Retrieve the proof. + let proof = + serde_json::from_value(execution.get_mut("proof").unwrap_or(&mut serde_json::Value::Null).take()) + .map_err(de::Error::custom)?; // Recover the execution. - Self::from(transitions.into_iter(), global_state_root, inclusion_proof).map_err(de::Error::custom) + Self::from(transitions.into_iter(), global_state_root, proof).map_err(de::Error::custom) } false => FromBytesDeserializer::::deserialize_with_size_encoding(deserializer, "execution"), } diff --git a/synthesizer/src/block/transaction/fee/bytes.rs b/synthesizer/src/block/transaction/fee/bytes.rs index 1906ecd6f1..6a28df2113 100644 --- a/synthesizer/src/block/transaction/fee/bytes.rs +++ b/synthesizer/src/block/transaction/fee/bytes.rs @@ -29,16 +29,16 @@ impl FromBytes for Fee { let transition = Transition::read_le(&mut reader)?; // Read the global state root. let global_state_root = N::StateRoot::read_le(&mut reader)?; - // Read the inclusion proof variant. - let inclusion_variant = u8::read_le(&mut reader)?; - // Read the inclusion proof. - let inclusion_proof = match inclusion_variant { + // Read the proof variant. + let proof_variant = u8::read_le(&mut reader)?; + // Read the proof. + let proof = match proof_variant { 0 => None, 1 => Some(Proof::read_le(&mut reader)?), - _ => return Err(error("Invalid inclusion proof variant '{inclusion_variant}'")), + _ => return Err(error("Invalid proof variant '{proof_variant}'")), }; // Return the new `Fee` instance. - Ok(Self::from(transition, global_state_root, inclusion_proof)) + Ok(Self::from(transition, global_state_root, proof)) } } @@ -51,8 +51,8 @@ impl ToBytes for Fee { self.transition.write_le(&mut writer)?; // Write the global state root. self.global_state_root.write_le(&mut writer)?; - // Write the inclusion proof. - match self.inclusion_proof { + // Write the proof. + match self.proof { None => 0u8.write_le(&mut writer)?, Some(ref proof) => { 1u8.write_le(&mut writer)?; diff --git a/synthesizer/src/block/transaction/fee/mod.rs b/synthesizer/src/block/transaction/fee/mod.rs index c932703ede..0138ac6dbe 100644 --- a/synthesizer/src/block/transaction/fee/mod.rs +++ b/synthesizer/src/block/transaction/fee/mod.rs @@ -18,10 +18,16 @@ mod bytes; mod serialize; mod string; -use crate::{snark::Proof, Input, Transition}; +// use circuit::{Aleo, Identifier}; +use crate::{ + snark::{KeyBatch, Proof}, + Input, + Transition, +}; +use circuit::Assignment; use console::{ network::prelude::*, - program::{Literal, Plaintext}, + program::{Identifier, Literal, Plaintext}, types::U64, }; @@ -31,15 +37,15 @@ pub struct Fee { transition: Transition, /// The global state root. global_state_root: N::StateRoot, - /// The inclusion proof. - inclusion_proof: Option>, + /// The proof of transition and optional inclusion. + proof: Option>, } impl Fee { /// Initializes a new `Fee` instance with the given transition, global state root, and inclusion proof. - pub fn from(transition: Transition, global_state_root: N::StateRoot, inclusion_proof: Option>) -> Self { + pub fn from(transition: Transition, global_state_root: N::StateRoot, proof: Option>) -> Self { // Return the new `Fee` instance. - Self { transition, global_state_root, inclusion_proof } + Self { transition, global_state_root, proof } } /// Returns 'true' if the fee amount is zero. @@ -76,9 +82,50 @@ impl Fee { self.global_state_root } - /// Returns the inclusion proof. - pub const fn inclusion_proof(&self) -> Option<&Proof> { - self.inclusion_proof.as_ref() + /// Returns whether the fee proves inclusion + pub fn proves_inclusion(&self) -> bool { + if let Some(proof) = &self.proof { proof.proves_inclusion() } else { false } + } + + /// Returns the fee proof. + pub const fn proof(&self) -> Option<&Proof> { + self.proof.as_ref() + } + + pub fn prove( + &mut self, + batch: KeyBatch, + assignments: &[&[Assignment]], + function_names: Vec<&Identifier>, + rng: &mut R, + ) -> Result<()> { + if assignments.is_empty() || assignments.len() > 2 { + bail!("Expected 1 or 2 assignments, got {}", assignments.len()) + } + let inclusion_name = Identifier::::from_str(N::INCLUSION_FUNCTION_NAME)?; + if assignments.len() == 2 && *function_names[1] != inclusion_name { + bail!("Expected 2nd assignment to belong to inclusion") + } + let proves_inclusion = **function_names.last().unwrap() == inclusion_name; + let proof = batch.prove(function_names.as_slice(), assignments, proves_inclusion, rng)?; + + self.proof = Some(proof); + + Ok(()) + } + + /// Verifies the fee. + pub fn verify(&self, batch: KeyBatch, inputs: &[Vec>]) -> Result { + ensure!(self.proof.is_some(), "proof missing!"); + + let mut function_names = vec![self.transition().function_name()]; + let inclusion_name = Identifier::::from_str(N::INCLUSION_FUNCTION_NAME)?; + if inputs.len() > 1 { + function_names.push(&inclusion_name); + } + batch.verify(function_names.as_slice(), inputs, self.proof.as_ref().unwrap())?; + + Ok(true) } } diff --git a/synthesizer/src/block/transaction/fee/serialize.rs b/synthesizer/src/block/transaction/fee/serialize.rs index 427f2589d6..bc7ed3a905 100644 --- a/synthesizer/src/block/transaction/fee/serialize.rs +++ b/synthesizer/src/block/transaction/fee/serialize.rs @@ -26,9 +26,7 @@ impl Serialize for Fee { let mut fee = serializer.serialize_struct("Fee", 3)?; fee.serialize_field("transition", &self.transition)?; fee.serialize_field("global_state_root", &self.global_state_root)?; - if let Some(inclusion_proof) = &self.inclusion_proof { - fee.serialize_field("inclusion", inclusion_proof)?; - } + fee.serialize_field("proof", &self.proof)?; fee.end() } false => ToBytesSerializer::serialize_with_size_encoding(self, serializer), @@ -47,10 +45,10 @@ impl<'de, N: Network> Deserialize<'de> for Fee { let transition = DeserializeExt::take_from_value::(&mut fee, "transition")?; // Retrieve the global state root. let global_state_root = DeserializeExt::take_from_value::(&mut fee, "global_state_root")?; - // Retrieve the inclusion proof. - let inclusion_proof = DeserializeExt::take_from_value::(&mut fee, "inclusion")?; + // Retrieve the proof. + let proof = DeserializeExt::take_from_value::(&mut fee, "proof")?; // Recover the fee. - Ok(Self::from(transition, global_state_root, inclusion_proof)) + Ok(Self::from(transition, global_state_root, proof)) } false => FromBytesDeserializer::::deserialize_with_size_encoding(deserializer, "fee"), } diff --git a/synthesizer/src/block/transition/bytes.rs b/synthesizer/src/block/transition/bytes.rs index d6cd59e348..6ffe9836d4 100644 --- a/synthesizer/src/block/transition/bytes.rs +++ b/synthesizer/src/block/transition/bytes.rs @@ -70,16 +70,13 @@ impl FromBytes for Transition { 2.. => return Err(error(format!("Invalid transition finalize variant ({finalize_variant})"))), }; - // Read the proof. - let proof = FromBytes::read_le(&mut reader)?; - // Read the transition public key. let tpk = FromBytes::read_le(&mut reader)?; // Read the transition commitment. let tcm = FromBytes::read_le(&mut reader)?; // Construct the candidate transition. - let transition = Self::new(program_id, function_name, inputs, outputs, finalize, proof, tpk, tcm) + let transition = Self::new(program_id, function_name, inputs, outputs, finalize, tpk, tcm) .map_err(|e| error(e.to_string()))?; // Ensure the transition ID matches the expected ID. match transition_id == *transition.id() { @@ -128,9 +125,6 @@ impl ToBytes for Transition { } } - // Write the proof. - self.proof.write_le(&mut writer)?; - // Write the transition public key. self.tpk.write_le(&mut writer)?; // Write the transition commitment. diff --git a/synthesizer/src/block/transition/mod.rs b/synthesizer/src/block/transition/mod.rs index 0fd827f4ba..1ac051851f 100644 --- a/synthesizer/src/block/transition/mod.rs +++ b/synthesizer/src/block/transition/mod.rs @@ -46,7 +46,6 @@ use console::{ }, types::{Field, Group}, }; -use snarkvm_synthesizer_snark::Proof; #[derive(Clone, PartialEq, Eq)] pub struct Transition { @@ -62,8 +61,6 @@ pub struct Transition { outputs: Vec>, /// The inputs for finalize. finalize: Option>>, - /// The transition proof. - proof: Proof, /// The transition public key. tpk: Group, /// The transition commitment. @@ -79,14 +76,13 @@ impl Transition { inputs: Vec>, outputs: Vec>, finalize: Option>>, - proof: Proof, tpk: Group, tcm: Field, ) -> Result { // Compute the transition ID. let id = *Self::function_tree(&inputs, &outputs)?.root(); // Return the transition. - Ok(Self { id: id.into(), program_id, function_name, inputs, outputs, finalize, proof, tpk, tcm }) + Ok(Self { id: id.into(), program_id, function_name, inputs, outputs, finalize, tpk, tcm }) } /// Initializes a new transition from a request and response. @@ -96,7 +92,6 @@ impl Transition { finalize: Option>>, output_types: &[ValueType], output_registers: &[Option>], - proof: Proof, ) -> Result { let network_id = *request.network_id(); let program_id = *request.program_id(); @@ -256,7 +251,7 @@ impl Transition { // Retrieve the `tcm`. let tcm = *request.tcm(); // Return the transition. - Self::new(program_id, function_name, inputs, outputs, finalize, proof, tpk, tcm) + Self::new(program_id, function_name, inputs, outputs, finalize, tpk, tcm) } } @@ -291,11 +286,6 @@ impl Transition { self.finalize.as_ref() } - /// Returns the proof. - pub const fn proof(&self) -> &Proof { - &self.proof - } - /// Returns the transition public key. pub const fn tpk(&self) -> &Group { &self.tpk @@ -440,3 +430,25 @@ impl Transition { self.finalize.into_iter().flatten() } } + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ProvingKeyId { + pub program_id: ProgramID, + pub function_name: Identifier, +} + +impl Ord for ProvingKeyId { + /// Ordering is determined by the program_id first, then the function_name second. + fn cmp(&self, other: &Self) -> Ordering { + match self.program_id == other.program_id { + true => self.function_name.cmp(&other.function_name), + false => self.program_id.cmp(&other.program_id), + } + } +} + +impl PartialOrd for ProvingKeyId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/synthesizer/src/block/transition/serialize.rs b/synthesizer/src/block/transition/serialize.rs index 6d6399fe3a..9f0b963500 100644 --- a/synthesizer/src/block/transition/serialize.rs +++ b/synthesizer/src/block/transition/serialize.rs @@ -32,7 +32,6 @@ impl Serialize for Transition { if let Some(finalize) = &self.finalize { transition.serialize_field("finalize", &finalize)?; } - transition.serialize_field("proof", &self.proof)?; transition.serialize_field("tpk", &self.tpk)?; transition.serialize_field("tcm", &self.tcm)?; transition.end() @@ -67,8 +66,6 @@ impl<'de, N: Network> Deserialize<'de> for Transition { Some(finalize) => Some(serde_json::from_value(finalize.clone()).map_err(de::Error::custom)?), None => None, }, - // Retrieve the proof. - DeserializeExt::take_from_value::(&mut transition, "proof")?, // Retrieve the `tpk`. DeserializeExt::take_from_value::(&mut transition, "tpk")?, // Retrieve the `tcm`. diff --git a/synthesizer/src/process/execute.rs b/synthesizer/src/process/execute.rs index b15cc5c0c1..566ca97c42 100644 --- a/synthesizer/src/process/execute.rs +++ b/synthesizer/src/process/execute.rs @@ -16,51 +16,53 @@ use super::*; +use crate::{ + snark::{Key, KeyBatch, KeyMode}, + ProvingKeyId, +}; + impl Process { /// Executes the given authorization. #[inline] pub fn execute, R: Rng + CryptoRng>( &self, - authorization: Authorization, + execution: &mut Execution, + transition_assignments: BTreeMap, Vec>>, + inclusion_assignments: Option>>, rng: &mut R, - ) -> Result<(Response, Execution, Inclusion, Vec>)> { + ) -> Result<()> { let timer = timer!("Process::execute"); - // Retrieve the main request (without popping it). - let request = authorization.peek_next()?; - - #[cfg(feature = "aleo-cli")] - println!("{}", format!(" • Executing '{}/{}'...", request.program_id(), request.function_name()).dimmed()); - - // Initialize the execution. - let execution = Arc::new(RwLock::new(Execution::new())); - // Initialize the inclusion. - let inclusion = Arc::new(RwLock::new(Inclusion::new())); - // Initialize the metrics. - let metrics = Arc::new(RwLock::new(Vec::new())); - // Initialize the call stack. - let call_stack = CallStack::execute(authorization, execution.clone(), inclusion.clone(), metrics.clone())?; - lap!(timer, "Initialize call stack"); - // Execute the circuit. - let response = self.get_stack(request.program_id())?.execute_function::(call_stack, rng)?; - lap!(timer, "Execute the function"); - // Extract the execution. - let execution = Arc::try_unwrap(execution).unwrap().into_inner(); - // Ensure the execution is not empty. - ensure!(!execution.is_empty(), "Execution of '{}/{}' is empty", request.program_id(), request.function_name()); - // Extract the inclusion. - let inclusion = Arc::try_unwrap(inclusion).unwrap().into_inner(); - // Extract the metrics. - let metrics = Arc::try_unwrap(metrics).unwrap().into_inner(); + // Retrieve the proving keys and fill the batch. + let mut batch = KeyBatch::new(1 + transition_assignments.len(), KeyMode::Proving); + let mut assignments = Vec::with_capacity(1 + transition_assignments.len()); + let mut function_names = Vec::with_capacity(1 + transition_assignments.len()); + for (proving_key_id, transition_assignments) in transition_assignments.iter() { + let proving_key = self.get_proving_key(proving_key_id.program_id, proving_key_id.function_name)?; + // NOTE: consistent ordering of keys and assignments is crucial + batch.add(Key::ProvingKey(proving_key))?; + assignments.push(transition_assignments.as_slice()); + function_names.push(&proving_key_id.function_name); + } + + let inclusion_name = Identifier::::from_str(N::INCLUSION_FUNCTION_NAME)?; + if let Some(inclusion_assignments) = inclusion_assignments.as_ref() { + let inclusion_key = ProvingKey::::new(N::inclusion_proving_key().clone()); + batch.add(Key::ProvingKey(inclusion_key))?; + assignments.push(inclusion_assignments); + function_names.push(&inclusion_name); + } + + execution.prove(batch, assignments.as_slice(), function_names.as_slice(), rng)?; finish!(timer); - Ok((response, execution, inclusion, metrics)) + Ok(()) } /// Verifies the given execution is valid. /// Note: This does *not* check that the global state root exists in the ledger. #[inline] - pub fn verify_execution(&self, execution: &Execution) -> Result<()> { + pub fn verify_execution(&self, execution: &Execution) -> Result<()> { let timer = timer!("Process::verify_execution"); // Ensure the execution contains transitions. @@ -82,14 +84,11 @@ impl Process { } lap!(timer, "Verify the number of transitions"); - // Ensure the inclusion proof is valid. - if VERIFY_INCLUSION { - Inclusion::verify_execution(execution)?; - lap!(timer, "Verify the inclusion proof"); - } - // Replicate the execution stack for verification. let mut queue = execution.clone(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + let mut batch = KeyBatch::::new(1 + execution.transitions().len(), KeyMode::Verifying); + let mut all_inputs = Vec::with_capacity(1 + execution.transitions().len()); // Verify each transition. while let Ok(transition) = queue.pop() { @@ -203,22 +202,40 @@ impl Process { } } + let pk_id = + ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + if let Some(assignment) = transition_assignments.get_mut(&pk_id) { + assignment.push(inputs); + } else { + transition_assignments.insert(pk_id, vec![inputs]); + } + lap!(timer, "Construct the verifier inputs"); #[cfg(debug_assertions)] - println!("Transition public inputs ({} elements): {:#?}", inputs.len(), inputs); + println!("Transition public inputs ({} elements): {:#?}", all_inputs.len(), all_inputs); + lap!(timer, "Verify transition for {}", function.name()); + } + + for (pkid, circuit_inputs) in transition_assignments { // Retrieve the verifying key. - let verifying_key = self.get_verifying_key(stack.program_id(), function.name())?; - // Ensure the transition proof is valid. - ensure!( - verifying_key.verify(&function.name().to_string(), &inputs, transition.proof()), - "Transition is invalid - failed to verify transition proof" - ); + let verifying_key = self.get_verifying_key(pkid.program_id, pkid.function_name)?; + batch.add(Key::VerifyingKey(verifying_key))?; + all_inputs.push(circuit_inputs); + } - lap!(timer, "Verify transition proof for {}", function.name()); + // Ensure the inclusion proof is valid. + if execution.proves_inclusion() { + let (inclusion_vk, inclusion_inputs) = Inclusion::prepare_verify_execution(execution)?; + batch.add(Key::VerifyingKey(inclusion_vk))?; + all_inputs.push(inclusion_inputs); } + // Ensure the transition proofs are all valid. + ensure!(execution.verify(batch, all_inputs.as_slice())?, "Execution is invalid - failed to verify proof"); + lap!(timer, "Verify execution proof"); + finish!(timer); Ok(()) } diff --git a/synthesizer/src/process/execute_fee.rs b/synthesizer/src/process/execute_fee.rs index 47f6a39538..04d597e6ad 100644 --- a/synthesizer/src/process/execute_fee.rs +++ b/synthesizer/src/process/execute_fee.rs @@ -15,73 +15,54 @@ // along with the snarkVM library. If not, see . use super::*; +use crate::snark::{Key, KeyBatch, KeyMode}; impl Process { - /// Executes the fee given the credits record and the fee amount (in microcredits). + /// Executes the given fee. #[inline] pub fn execute_fee, R: Rng + CryptoRng>( &self, - private_key: &PrivateKey, - credits: Record>, - fee_in_microcredits: u64, + fee: &mut Fee, + fee_assignments: &[Assignment], + inclusion_assignments: Option>>, rng: &mut R, - ) -> Result<(Response, Transition, Inclusion, Vec>)> { + ) -> Result<()> { let timer = timer!("Process::execute_fee"); + let fee_program_id = fee.transition().program_id(); + let fee_function_name = *fee.transition().function_name(); + let inclusion_name = Identifier::::from_str(N::INCLUSION_FUNCTION_NAME)?; + // Ensure the fee has the correct program ID. - let program_id = ProgramID::from_str("credits.aleo")?; + let fee_program_id_test = ProgramID::from_str("credits.aleo")?; + ensure!(*fee_program_id == fee_program_id_test, "Incorrect program ID for fee"); + // Ensure the fee has the correct function. - let function_name = Identifier::from_str("fee")?; - - // Retrieve the input types. - let input_types = self.get_program(program_id)?.get_function(&function_name)?.input_types(); - // Construct the inputs. - let inputs = [Value::Record(credits), Value::from_str(&U64::::new(fee_in_microcredits).to_string())?]; - lap!(timer, "Construct the inputs"); - // Compute the request. - let request = Request::sign(private_key, program_id, function_name, inputs.iter(), &input_types, rng)?; - lap!(timer, "Compute the request"); - // Initialize the authorization. - let authorization = Authorization::new(&[request.clone()]); - lap!(timer, "Initialize the authorization"); - // Construct the call stack. - let call_stack = CallStack::Authorize(vec![request], *private_key, authorization.clone()); - // Construct the authorization from the function. - let _response = self.get_stack(program_id)?.execute_function::(call_stack, rng)?; - lap!(timer, "Construct the authorization from the function"); - - // Retrieve the main request (without popping it). - let request = authorization.peek_next()?; - // Prepare the stack. - let stack = self.get_stack(request.program_id())?; - - #[cfg(feature = "aleo-cli")] - println!("{}", format!(" • Calling '{}/{}'...", request.program_id(), request.function_name()).dimmed()); - - // Initialize the execution. - let execution = Arc::new(RwLock::new(Execution::new())); - // Initialize the inclusion. - let inclusion = Arc::new(RwLock::new(Inclusion::new())); - // Initialize the metrics. - let metrics = Arc::new(RwLock::new(Vec::new())); - // Initialize the call stack. - let call_stack = CallStack::execute(authorization, execution.clone(), inclusion.clone(), metrics.clone())?; - // Execute the circuit. - let response = stack.execute_function::(call_stack, rng)?; - lap!(timer, "Execute the circuit"); - - // Extract the execution. - let execution = Arc::try_unwrap(execution).unwrap().into_inner(); - // Ensure the execution contains 1 transition. - ensure!(execution.len() == 1, "Execution of '{}/{}' does not contain 1 transition", program_id, function_name); - // Extract the inclusion. - let inclusion = Arc::try_unwrap(inclusion).unwrap().into_inner(); - // Extract the metrics. - let metrics = Arc::try_unwrap(metrics).unwrap().into_inner(); + let fee_function = Identifier::from_str("fee")?; + ensure!(fee_function_name == fee_function, "Incorrect function name for fee"); + + // Ensure the assignments are not empty. + ensure!(!fee_assignments.is_empty(), "Expected assignments"); + + let mut batch = KeyBatch::new(2, KeyMode::Proving); + let mut assignments = Vec::with_capacity(2); + let mut function_names = Vec::with_capacity(2); + let proving_key = self.get_proving_key(fee_program_id, fee_function_name)?; + batch.add(Key::ProvingKey(proving_key))?; + assignments.push(fee_assignments); + function_names.push(&fee_function_name); + + if let Some(inclusion_assignments) = inclusion_assignments.as_ref() { + let proving_key = ProvingKey::::new(N::inclusion_proving_key().clone()); + batch.add(Key::ProvingKey(proving_key))?; + assignments.push(inclusion_assignments); + function_names.push(&inclusion_name); + } - finish!(timer); + fee.prove(batch, assignments.as_slice(), function_names, rng)?; - Ok((response, execution.peek()?.clone(), inclusion, metrics)) + finish!(timer); + Ok(()) } /// Verifies the given fee is valid. @@ -121,6 +102,10 @@ impl Process { } lap!(timer, "Verify the inputs"); + // We need to verify 2 parts: the transition and inclusion + let mut batch = KeyBatch::::new(2, KeyMode::Verifying); + let mut all_inputs = Vec::with_capacity(2); + // Ensure each output is valid. let num_inputs = fee.inputs().len(); if fee @@ -133,10 +118,6 @@ impl Process { } lap!(timer, "Verify the outputs"); - // Ensure the inclusion proof is valid. - Inclusion::verify_fee(fee)?; - lap!(timer, "Verify the inclusion proof"); - // Compute the x- and y-coordinate of `tpk`. let (tpk_x, tpk_y) = fee.tpk().to_xy_coordinates(); @@ -168,11 +149,23 @@ impl Process { // Retrieve the verifying key. let verifying_key = self.get_verifying_key(stack.program_id(), function.name())?; - // Ensure the transition proof is valid. - ensure!( - verifying_key.verify(&function.name().to_string(), &inputs, fee.proof()), - "Fee is invalid - failed to verify transition proof" - ); + batch.add(Key::VerifyingKey(verifying_key))?; + all_inputs.push(vec![inputs]); + + // Get inclusion proof vk and inputs. + if fee.proves_inclusion() { + let (inclusion_vk, inclusion_inputs) = Inclusion::prepare_verify_fee(fee)?; + batch.add(Key::VerifyingKey(inclusion_vk))?; + all_inputs.push(inclusion_inputs); + } + lap!(timer, "Get the inclusion proof vk and inputs"); + + // Ensure there is a proof. + let proof = fee.proof(); + ensure!(proof.is_some(), "Fee is invalid - missing proof"); + + ensure!(fee.verify(batch, all_inputs.as_slice())?, "Fee is invalid - failed to verify proof"); + lap!(timer, "Verify the transition proof"); finish!(timer); diff --git a/synthesizer/src/process/mod.rs b/synthesizer/src/process/mod.rs index cc9228f471..2cb2eb67b7 100644 --- a/synthesizer/src/process/mod.rs +++ b/synthesizer/src/process/mod.rs @@ -23,6 +23,7 @@ mod evaluate; mod execute; mod execute_fee; mod finalize; +mod prepare; #[cfg(test)] mod tests; @@ -42,9 +43,10 @@ use console::{ use snarkvm_synthesizer_snark::{ProvingKey, UniversalSRS, VerifyingKey}; use aleo_std::prelude::{finish, lap, timer}; +use circuit::Assignment; use indexmap::IndexMap; use parking_lot::RwLock; -use std::sync::Arc; +use std::{collections::BTreeMap, sync::Arc}; #[cfg(test)] use std::collections::HashMap; @@ -309,7 +311,8 @@ impl Process { #[cfg(any(test, feature = "test"))] pub mod test_helpers { use super::*; - use crate::{Process, Program, Transition}; + use crate::{Process, Program, ProvingKeyId, Transition}; + use console::{account::PrivateKey, network::Testnet3, program::Identifier}; use once_cell::sync::OnceCell; @@ -400,8 +403,21 @@ function compute: .unwrap(); assert_eq!(authorization.len(), 1); // Execute the request. - let (_response, execution, _inclusion, _metrics) = - process.execute::(authorization, rng).unwrap(); + let (_response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { + program_id: *transition.program_id(), + function_name: *transition.function_name(), + }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); assert_eq!(execution.len(), 1); // Return the execution. execution diff --git a/synthesizer/src/process/prepare.rs b/synthesizer/src/process/prepare.rs new file mode 100644 index 0000000000..5150ef5063 --- /dev/null +++ b/synthesizer/src/process/prepare.rs @@ -0,0 +1,150 @@ +// Copyright (C) 2019-2023 Aleo Systems Inc. +// This file is part of the snarkVM library. + +// The snarkVM library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The snarkVM library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the snarkVM library. If not, see . + +use circuit::Assignment; + +use super::*; +use std::collections::VecDeque; + +impl Process { + /// Prepares a call to the program function for the given inputs. + #[inline] + pub fn prepare_function>( + &self, + authorization: Authorization, + ) -> Result<(Response, Execution, Inclusion, Vec>, VecDeque>)> { + let timer = timer!("Process::prepare"); + + // Retrieve the main request (without popping it). + let request = authorization.peek_next()?; + + #[cfg(feature = "aleo-cli")] + println!("{}", format!(" • Executing '{}/{}'...", request.program_id(), request.function_name()).dimmed()); + + // Initialize the execution. + let execution = Arc::new(RwLock::new(Execution::new())); + // Initialize the inclusion. + let inclusion = Arc::new(RwLock::new(Inclusion::new())); + // Initialize the metrics. + let metrics = Arc::new(RwLock::new(Vec::new())); + // Initialize the call stack. + let assignments = Assignments::::default(); + let call_stack = CallStack::prepare( + authorization, + execution.clone(), + inclusion.clone(), + metrics.clone(), + assignments.clone(), + )?; + + lap!(timer, "Initialize call stack"); + // Execute the circuit. + let response = self.get_stack(request.program_id())?.execute_function::(call_stack)?; + lap!(timer, "Synthesize the circuit"); + // Extract the execution. + let execution = Arc::try_unwrap(execution).unwrap().into_inner(); + // Ensure the execution is not empty. + ensure!(!execution.is_empty(), "Execution of '{}/{}' is empty", request.program_id(), request.function_name()); + // Extract the inclusion. + let inclusion = Arc::try_unwrap(inclusion).unwrap().into_inner(); + // Extract the metrics. + let metrics = Arc::try_unwrap(metrics).unwrap().into_inner(); + // Extract the assignments. + let assignments = Arc::try_unwrap(assignments).unwrap_or_default().into_inner(); + + finish!(timer); + Ok((response, execution, inclusion, metrics, assignments)) + } + + /// Prepares a fee for the given executions. + #[inline] + pub fn prepare_fee, R: Rng + CryptoRng>( + &self, + private_key: &PrivateKey, + credits: Record>, + fee_in_microcredits: u64, + rng: &mut R, + ) -> Result<(Response, Transition, Inclusion, VecDeque>, Vec>)> { + let timer = timer!("Process::prepare_fee"); + + // Ensure the fee has the correct program ID. + let program_id = ProgramID::from_str("credits.aleo")?; + // Ensure the fee has the correct function. + let function_name = Identifier::from_str("fee")?; + + // Retrieve the input types. + let input_types = self.get_program(program_id)?.get_function(&function_name)?.input_types(); + // Construct the inputs. + let inputs = [Value::Record(credits), Value::from_str(&U64::::new(fee_in_microcredits).to_string())?]; + lap!(timer, "Construct the inputs"); + // Compute the request. + let request = Request::sign(private_key, program_id, function_name, inputs.iter(), &input_types, rng)?; + lap!(timer, "Compute the request"); + // Initialize the authorization. + let authorization = Authorization::new(&[request.clone()]); + lap!(timer, "Initialize the authorization"); + // Construct the call stack. + let call_stack = CallStack::Authorize(vec![request], *private_key, authorization.clone()); + // Construct the authorization from the function. + let _response = self.get_stack(program_id)?.execute_function::(call_stack)?; + lap!(timer, "Construct the authorization from the function"); + + // Retrieve the main request (without popping it). + let request = authorization.peek_next()?; + // Prepare the stack. + let stack = self.get_stack(request.program_id())?; + + #[cfg(feature = "aleo-cli")] + println!("{}", format!(" • Calling '{}/{}'...", request.program_id(), request.function_name()).dimmed()); + + // Initialize the execution. + let execution = Arc::new(RwLock::new(Execution::new())); + // Initialize the inclusion. + let inclusion = Arc::new(RwLock::new(Inclusion::new())); + // Initialize the metrics. + let metrics = Arc::new(RwLock::new(Vec::new())); + // Initialize the call stack. + let assignments = Assignments::::default(); + // Pass registers to Callstack + let call_stack = CallStack::prepare( + authorization, + execution.clone(), + inclusion.clone(), + metrics.clone(), + assignments.clone(), + )?; + // Execute the circuit. + let response = stack.execute_function::(call_stack)?; + lap!(timer, "Execute the circuit"); + + // Extract the execution. + let mut execution = Arc::try_unwrap(execution).unwrap().into_inner(); + // Ensure the execution contains 1 transition. + ensure!(execution.len() == 1, "Execution of '{}/{}' does not contain 1 transition", program_id, function_name); + // Extract the inclusion. + let inclusion = Arc::try_unwrap(inclusion).unwrap().into_inner(); + // Extract the metrics. + let metrics = Arc::try_unwrap(metrics).unwrap().into_inner(); + // Extract the assignments + let assignments = Arc::try_unwrap(assignments).unwrap_or_default().into_inner(); + // Extract the transition + let transition = execution.pop()?; + + finish!(timer); + + Ok((response, transition, inclusion, assignments, metrics)) + } +} diff --git a/synthesizer/src/process/stack/authorize.rs b/synthesizer/src/process/stack/authorize.rs index 1a78a4082f..9f70aecb50 100644 --- a/synthesizer/src/process/stack/authorize.rs +++ b/synthesizer/src/process/stack/authorize.rs @@ -56,7 +56,7 @@ impl Stack { // Construct the call stack. let call_stack = CallStack::Authorize(vec![request], *private_key, authorization.clone()); // Construct the authorization from the function. - let _response = self.execute_function::(call_stack, rng)?; + let _response = self.execute_function::(call_stack)?; lap!(timer, "Construct the authorization from the function"); finish!(timer); diff --git a/synthesizer/src/process/stack/deploy.rs b/synthesizer/src/process/stack/deploy.rs index 348855ecd4..354201bdb5 100644 --- a/synthesizer/src/process/stack/deploy.rs +++ b/synthesizer/src/process/stack/deploy.rs @@ -113,10 +113,10 @@ impl Stack { // Initialize the call stack. let call_stack = CallStack::CheckDeployment(vec![request], burner_private_key, assignments.clone()); // Synthesize the circuit. - let _response = self.execute_function::(call_stack, rng)?; + let _response = self.execute_function::(call_stack)?; lap!(timer, "Synthesize the circuit"); // Check the certificate. - match assignments.read().last() { + match assignments.read().back() { None => { bail!("The assignment for function '{}' is missing in '{program_id}'", function.name()) } diff --git a/synthesizer/src/process/stack/evaluate.rs b/synthesizer/src/process/stack/evaluate.rs index b4a2e2854a..f16c5c1536 100644 --- a/synthesizer/src/process/stack/evaluate.rs +++ b/synthesizer/src/process/stack/evaluate.rs @@ -97,8 +97,8 @@ impl StackEvaluate for Stack { // Retrieve the next request, based on the call stack mode. let (request, call_stack) = match &call_stack { CallStack::Evaluate(authorization) => (authorization.next()?, call_stack), - CallStack::Execute(authorization, ..) => (authorization.peek_next()?, call_stack.replicate()), - _ => bail!("Illegal operation: call stack must be `Evaluate` or `Execute` in `evaluate_function`."), + CallStack::Prepare(authorization, ..) => (authorization.peek_next()?, call_stack.replicate()), + _ => bail!("Illegal operation: call stack must be `Evaluate` or `Prepare` in `evaluate_function`."), }; lap!(timer, "Retrieve the next request"); diff --git a/synthesizer/src/process/stack/execute.rs b/synthesizer/src/process/stack/execute.rs index fb13b42f28..60517f7d9a 100644 --- a/synthesizer/src/process/stack/execute.rs +++ b/synthesizer/src/process/stack/execute.rs @@ -54,8 +54,8 @@ impl StackExecute for Stack { // Store the inputs. closure.inputs().iter().map(|i| i.register()).zip_eq(inputs).try_for_each(|(register, input)| { - // If the circuit is in execute mode, then store the console input. - if let CallStack::Execute(..) = registers.call_stack() { + // If the circuit is in prepare mode, then store the console input. + if let CallStack::Prepare(..) = registers.call_stack() { use circuit::Eject; // Assign the console input to the register. registers.store(self, register, input.eject_value())?; @@ -67,8 +67,8 @@ impl StackExecute for Stack { // Execute the instructions. for instruction in closure.instructions() { - // If the circuit is in execute mode, then evaluate the instructions. - if let CallStack::Execute(..) = registers.call_stack() { + // If the circuit is in prepare mode, then evaluate the instructions. + if let CallStack::Prepare(..) = registers.call_stack() { // If the evaluation fails, bail and return the error. if let Err(error) = instruction.evaluate(self, &mut registers) { bail!("Failed to evaluate instruction ({instruction}): {error}"); @@ -122,11 +122,7 @@ impl StackExecute for Stack { /// # Errors /// This method will halt if the given inputs are not the same length as the input statements. #[inline] - fn execute_function, R: Rng + CryptoRng>( - &self, - mut call_stack: CallStack, - rng: &mut R, - ) -> Result> { + fn execute_function>(&self, mut call_stack: CallStack) -> Result> { let timer = timer!("Stack::execute_function"); // Ensure the call stack is not `Evaluate`. @@ -206,8 +202,8 @@ impl StackExecute for Stack { // Store the inputs. function.inputs().iter().map(|i| i.register()).zip_eq(request.inputs()).try_for_each(|(register, input)| { - // If the circuit is in execute mode, then store the console input. - if let CallStack::Execute(..) = registers.call_stack() { + // If the circuit is in prepare mode, then store the console input. + if let CallStack::Prepare(..) = registers.call_stack() { // Assign the console input to the register. registers.store(self, register, input.eject_value())?; } @@ -221,8 +217,8 @@ impl StackExecute for Stack { // Execute the instructions. for instruction in function.instructions() { - // If the circuit is in execute mode, then evaluate the instructions. - if let CallStack::Execute(..) = registers.call_stack() { + // If the circuit is in prepare mode, then evaluate the instructions. + if let CallStack::Prepare(..) = registers.call_stack() { // If the evaluation fails, bail and return the error. if let Err(error) = instruction.evaluate(self, &mut registers) { bail!("Failed to evaluate instruction ({instruction}): {error}"); @@ -311,10 +307,10 @@ impl StackExecute for Stack { let num_response_constraints = A::num_constraints().saturating_sub(num_request_constraints).saturating_sub(num_function_constraints); - // If the circuit is in `Execute` mode, then prepare the 'finalize' scope if it exists. + // If the circuit is in `Synthesize`, `CheckDeployment` or `Prepare` mode, then prepare the 'finalize' scope if it exists. let finalize = if matches!(registers.call_stack(), CallStack::Synthesize(..)) || matches!(registers.call_stack(), CallStack::CheckDeployment(..)) - || matches!(registers.call_stack(), CallStack::Execute(..)) + || matches!(registers.call_stack(), CallStack::Prepare(..)) { // If this function has the finalize command, then construct the finalize inputs. if let Some(command) = function.finalize_command() { @@ -389,8 +385,8 @@ impl StackExecute for Stack { self.matches_value_type(output, output_type) })?; - // If the circuit is in `Execute` mode, then ensure the circuit is satisfied. - if let CallStack::Execute(..) = registers.call_stack() { + // If the circuit is in `Prepare` mode, then ensure the circuit is satisfied. + if let CallStack::Prepare(..) = registers.call_stack() { // If the circuit is empty or not satisfied, then throw an error. ensure!( A::num_constraints() > 0 && A::is_satisfied(), @@ -404,9 +400,9 @@ impl StackExecute for Stack { // Eject the circuit assignment and reset the circuit. let assignment = A::eject_assignment_and_reset(); - // If the circuit is in `Synthesize` or `Execute` mode, synthesize the circuit key, if it does not exist. + // If the circuit is in `Synthesize` or `Prepare` mode, synthesize the circuit key, if it does not exist. if matches!(registers.call_stack(), CallStack::Synthesize(..)) - || matches!(registers.call_stack(), CallStack::Execute(..)) + || matches!(registers.call_stack(), CallStack::Prepare(..)) { // If the proving key does not exist, then synthesize it. if !self.contains_proving_key(function.name()) { @@ -416,31 +412,22 @@ impl StackExecute for Stack { } } - // If the circuit is in `CheckDeployment` mode, then save the assignment. - if let CallStack::CheckDeployment(_, _, ref assignments) = registers.call_stack() { - // Add the assignment to the assignments. - assignments.write().push(assignment); - lap!(timer, "Save the circuit assignment"); - } - // If the circuit is in `Execute` mode, then execute the circuit into a transition. - else if let CallStack::Execute(_, ref execution, ref inclusion, ref metrics) = registers.call_stack() { + // If the circuit is in `Prepare` mode, then save the assignment. + if let CallStack::Prepare(_, ref execution, ref inclusion, ref metrics, ref assignments) = + registers.call_stack() + { registers.ensure_console_and_circuit_registers_match()?; - // Retrieve the proving key. - let proving_key = self.get_proving_key(function.name())?; - // Execute the circuit. - let proof = match proving_key.prove(&function.name().to_string(), &assignment, rng) { - Ok(proof) => proof, - Err(error) => bail!("Execution proof failed - {error}"), - }; - lap!(timer, "Execute the circuit"); + // Add the assignment to the assignments. + assignments.write().push_back(assignment); + lap!(timer, "Save the circuit assignment"); // Construct the transition. - let transition = - Transition::from(&console_request, &response, finalize, &output_types, &output_registers, proof)?; + let transition = Transition::from(&console_request, &response, finalize, &output_types, &output_registers)?; // Add the transition commitments. inclusion.write().insert_transition(console_request.input_ids(), &transition)?; + // Add the transition to the execution. execution.write().push(transition); @@ -454,6 +441,12 @@ impl StackExecute for Stack { num_response_constraints, }); } + // If the circuit is in `CheckDeployment` mode, then save the assignment. + else if let CallStack::CheckDeployment(_, _, ref assignments) = registers.call_stack() { + // Add the assignment to the assignments. + assignments.write().push_back(assignment); + lap!(timer, "Save the circuit assignment"); + } finish!(timer); diff --git a/synthesizer/src/process/stack/helpers/synthesize.rs b/synthesizer/src/process/stack/helpers/synthesize.rs index 2470b9ff76..0dbfaa43c5 100644 --- a/synthesizer/src/process/stack/helpers/synthesize.rs +++ b/synthesizer/src/process/stack/helpers/synthesize.rs @@ -60,7 +60,7 @@ impl Stack { // Initialize the call stack. let call_stack = CallStack::Synthesize(vec![request], burner_private_key, authorization); // Synthesize the circuit. - let _response = self.execute_function::(call_stack, rng)?; + let _response = self.execute_function::(call_stack)?; // Ensure the proving key exists. ensure!(self.contains_proving_key(function_name), "Function '{function_name}' is missing a proving key."); diff --git a/synthesizer/src/process/stack/inclusion/execute.rs b/synthesizer/src/process/stack/inclusion/execute.rs index c2d3ec75dd..53e75b66e2 100644 --- a/synthesizer/src/process/stack/inclusion/execute.rs +++ b/synthesizer/src/process/stack/inclusion/execute.rs @@ -21,10 +21,8 @@ macro_rules! prepare_execution_impl { // 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'") - } + // Ensure the proof in the execution is 'None'. + ensure!($execution.proof().is_none(), "Proof in the execution should not be set yet"); // Initialize an empty transaction tree. let mut transaction_tree = N::merkle_tree_bhp::(&[])?; @@ -132,48 +130,16 @@ impl Inclusion { } impl Inclusion { - /// Returns a new execution with an inclusion proof, for the given execution. - pub fn prove_execution, R: Rng + CryptoRng>( - &self, - 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 => { - // Fetch the inclusion proving key. - let proving_key = ProvingKey::::new(N::inclusion_proving_key().clone()); - - // 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)) - } - } - } - /// 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(execution: &Execution) -> Result<()> { + pub fn prepare_verify_execution(execution: &Execution) -> Result<(VerifyingKey, Vec>)> { // Retrieve the global state root. let global_state_root = execution.global_state_root(); - // Retrieve the inclusion proof. - let inclusion_proof = execution.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") + } // Initialize an empty transaction tree. let mut transaction_tree = N::merkle_tree_bhp::(&[])?; @@ -205,32 +171,9 @@ impl Inclusion { 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(()) + // Fetch the inclusion verifying key. + let verifying_key = VerifyingKey::::new(N::inclusion_verifying_key().clone()); + Ok((verifying_key, batch_verifier_inputs)) } } @@ -247,7 +190,11 @@ mod tests { match execution_transaction { Transaction::Execute(_, execution, _) => { - assert!(Inclusion::verify_execution(&execution).is_ok()); + let result = Inclusion::prepare_verify_execution(&execution); + assert!(result.is_ok()); + let (_vk, inputs) = result.unwrap(); + assert_eq!(inputs.len(), 1); + // We do not test inclusion individually, as we have tests to batch verify it with transitions elsewhere } _ => panic!("Expected an execution transaction"), } diff --git a/synthesizer/src/process/stack/inclusion/fee.rs b/synthesizer/src/process/stack/inclusion/fee.rs index c9a78dd6f9..721e5bbf09 100644 --- a/synthesizer/src/process/stack/inclusion/fee.rs +++ b/synthesizer/src/process/stack/inclusion/fee.rs @@ -75,9 +75,8 @@ macro_rules! prepare_fee_impl { bail!("Inclusion expected the global state root in the fee to *not* be zero") } // Ensure the assignments are not empty. - if assignments.is_empty() { - bail!("Inclusion expected the assignments for the fee to *not* be empty") - } + ensure!(!assignments.is_empty(), "Inclusion expected the assignments for the fee to *not* be empty"); + // Return the assignments. Ok(assignments) }}; @@ -104,51 +103,39 @@ impl Inclusion { prepare_fee_impl!(self, fee_transition, query, get_state_path_for_commitment_async, await) } - /// Returns a new fee with an inclusion proof, for the given transition. - pub fn prove_fee, R: Rng + CryptoRng>( - &self, - fee_transition: Transition, - assignments: &[InclusionAssignment], - rng: &mut R, - ) -> Result> { - // Ensure the fee has the correct program ID. - let fee_program_id = ProgramID::from_str("credits.aleo")?; - ensure!(*fee_transition.program_id() == fee_program_id, "Incorrect program ID for fee"); - - // Ensure the fee has the correct function. - let fee_function = Identifier::from_str("fee")?; - ensure!(*fee_transition.function_name() == fee_function, "Incorrect function name for fee"); + pub fn fee_global_state_root(assignments: &Vec>) -> Result { + // Initialize the global state root. + let mut global_state_root = N::StateRoot::default(); - // Ensure the assignments are not empty. - if assignments.is_empty() { - bail!("Inclusion expected the assignments for the fee to *not* be empty") + for assignment in assignments { + // Ensure the global state root is the same across iterations. + let assignment_global_state_root = assignment.global_state_root(); + if *global_state_root != Field::zero() && global_state_root != assignment_global_state_root { + bail!("Inclusion expected the global state root to be the same across iterations") + } + // Update the global state root. + global_state_root = assignment_global_state_root; } - // Fetch the inclusion proving key. - let proving_key = ProvingKey::::new(N::inclusion_proving_key().clone()); + // 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") + } - // Compute the inclusion batch proof. - let (global_state_root, inclusion_proof) = Self::prove_batch::(&proving_key, assignments, rng)?; - // Return the fee. - Ok(Fee::from(fee_transition, global_state_root, Some(inclusion_proof))) + Ok(global_state_root) } /// 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<()> { + pub fn prepare_verify_fee(fee: &Fee) -> Result<(VerifyingKey, Vec>)> { // Retrieve the global state root. let global_state_root = fee.global_state_root(); + // Ensure the global state root is not zero. if *global_state_root == Field::zero() { bail!("Inclusion expected the global state root in the fee to *not* be zero") } - // Retrieve the inclusion proof. - let inclusion_proof = match fee.inclusion_proof() { - Some(inclusion_proof) => inclusion_proof, - None => bail!("Inclusion expected the fee to contain an inclusion proof"), - }; - // Initialize an empty transaction tree. let transaction_tree = N::merkle_tree_bhp::(&[])?; // Initialize a vector for the batch verifier inputs. @@ -178,13 +165,7 @@ impl Inclusion { // 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(()) + Ok((verifying_key, batch_verifier_inputs)) } } @@ -201,7 +182,11 @@ mod tests { match deployment_transaction { Transaction::Deploy(_, _, _, fee) => { - assert!(Inclusion::verify_fee(&fee).is_ok()); + let result = Inclusion::prepare_verify_fee(&fee); + assert!(result.is_ok()); + let (_vk, inputs) = result.unwrap(); + assert_eq!(inputs.len(), 1); + // We do not test inclusion individually, as we have tests to batch verify it with transitions elsewhere } _ => panic!("Expected a deployment transaction"), } diff --git a/synthesizer/src/process/stack/inclusion/mod.rs b/synthesizer/src/process/stack/inclusion/mod.rs index 94eb7feb20..ea3476da81 100644 --- a/synthesizer/src/process/stack/inclusion/mod.rs +++ b/synthesizer/src/process/stack/inclusion/mod.rs @@ -25,7 +25,7 @@ use console::{ program::{Identifier, InputID, ProgramID, StatePath, TransactionLeaf, TransitionLeaf, TRANSACTION_DEPTH}, types::{Field, Group}, }; -use snarkvm_synthesizer_snark::{Proof, ProvingKey, VerifyingKey}; +use snarkvm_synthesizer_snark::VerifyingKey; use std::collections::HashMap; @@ -93,40 +93,6 @@ impl Inclusion { Ok(()) } - - /// Returns the global state root and inclusion proof for the given assignments. - fn prove_batch, R: Rng + CryptoRng>( - proving_key: &ProvingKey, - assignments: &[InclusionAssignment], - rng: &mut R, - ) -> Result<(N::StateRoot, Proof)> { - // Initialize the global state root. - let mut global_state_root = N::StateRoot::default(); - // Initialize a vector for the batch assignments. - let mut batch_assignments = vec![]; - - for assignment in assignments.iter() { - // Ensure the global state root is the same across iterations. - if *global_state_root != Field::zero() && global_state_root != assignment.state_path.global_state_root() { - bail!("Inclusion expected the global state root to be the same across iterations") - } - // Update the global state root. - global_state_root = assignment.state_path.global_state_root(); - - // Add the assignment to the assignments. - batch_assignments.push(assignment.to_circuit_assignment::()?); - } - - // 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") - } - - // Generate the inclusion batch proof. - let inclusion_proof = proving_key.prove_batch(N::INCLUSION_FUNCTION_NAME, &batch_assignments, rng)?; - // Return the global state root and inclusion proof. - Ok((global_state_root, inclusion_proof)) - } } pub struct InclusionAssignment { @@ -151,6 +117,10 @@ impl InclusionAssignment { Self { state_path, commitment, gamma, serial_number, local_state_root, is_global } } + pub fn global_state_root(&self) -> N::StateRoot { + self.state_path.global_state_root() + } + /// The circuit for state path verification. /// /// # Diagram diff --git a/synthesizer/src/process/stack/mod.rs b/synthesizer/src/process/stack/mod.rs index 0c33e49798..f5638ceaae 100644 --- a/synthesizer/src/process/stack/mod.rs +++ b/synthesizer/src/process/stack/mod.rs @@ -83,9 +83,9 @@ use snarkvm_synthesizer_snark::{Certificate, ProvingKey, UniversalSRS, Verifying use aleo_std::prelude::{finish, lap, timer}; use indexmap::IndexMap; use parking_lot::RwLock; -use std::sync::Arc; +use std::{collections::VecDeque, sync::Arc}; -pub type Assignments = Arc::Field>>>>; +pub type Assignments = Arc::Field>>>>; #[derive(Copy, Clone, Debug)] pub struct CallMetrics { @@ -103,7 +103,13 @@ pub enum CallStack { Synthesize(Vec>, PrivateKey, Authorization), CheckDeployment(Vec>, PrivateKey, Assignments), Evaluate(Authorization), - Execute(Authorization, Arc>>, Arc>>, Arc>>>), + Prepare( + Authorization, + Arc>>, + Arc>>, + Arc>>>, + Assignments, + ), } impl CallStack { @@ -112,14 +118,15 @@ impl CallStack { Ok(CallStack::Evaluate(authorization)) } - /// Initializes a call stack as `Self::Execute`. - pub fn execute( + /// Initializes a call stack as `Self::Prepare`. + pub fn prepare( authorization: Authorization, execution: Arc>>, inclusion: Arc>>, metrics: Arc>>>, + assignments: Assignments, ) -> Result { - Ok(CallStack::Execute(authorization, execution, inclusion, metrics)) + Ok(CallStack::Prepare(authorization, execution, inclusion, metrics, assignments)) } } @@ -139,11 +146,12 @@ impl CallStack { Arc::new(RwLock::new(assignments.read().clone())), ), CallStack::Evaluate(authorization) => CallStack::Evaluate(authorization.replicate()), - CallStack::Execute(authorization, execution, inclusion, metrics) => CallStack::Execute( + CallStack::Prepare(authorization, execution, inclusion, metrics, assignments) => CallStack::Prepare( authorization.replicate(), Arc::new(RwLock::new(execution.read().clone())), Arc::new(RwLock::new(inclusion.read().clone())), Arc::new(RwLock::new(metrics.read().clone())), + Arc::new(RwLock::new(assignments.read().clone())), ), } } @@ -155,7 +163,7 @@ impl CallStack { CallStack::Synthesize(requests, ..) => requests.push(request), CallStack::CheckDeployment(requests, ..) => requests.push(request), CallStack::Evaluate(authorization) => authorization.push(request), - CallStack::Execute(authorization, ..) => authorization.push(request), + CallStack::Prepare(authorization, ..) => authorization.push(request), } Ok(()) } @@ -169,7 +177,7 @@ impl CallStack { requests.pop().ok_or_else(|| anyhow!("No more requests on the stack")) } CallStack::Evaluate(authorization) => authorization.next(), - CallStack::Execute(authorization, ..) => authorization.next(), + CallStack::Prepare(authorization, ..) => authorization.next(), } } @@ -182,7 +190,7 @@ impl CallStack { requests.last().cloned().ok_or_else(|| anyhow!("No more requests on the stack")) } CallStack::Evaluate(authorization) => authorization.peek_next(), - CallStack::Execute(authorization, ..) => authorization.peek_next(), + CallStack::Prepare(authorization, ..) => authorization.peek_next(), } } } diff --git a/synthesizer/src/process/stack/traits.rs b/synthesizer/src/process/stack/traits.rs index da08e54dd1..8ffc000a5e 100644 --- a/synthesizer/src/process/stack/traits.rs +++ b/synthesizer/src/process/stack/traits.rs @@ -57,11 +57,7 @@ pub trait StackExecute { /// /// # Errors /// This method will halt if the given inputs are not the same length as the input statements. - fn execute_function, R: Rng + CryptoRng>( - &self, - call_stack: CallStack, - rng: &mut R, - ) -> Result>; + fn execute_function>(&self, call_stack: CallStack) -> Result>; } pub trait StackMatches { diff --git a/synthesizer/src/process/tests.rs b/synthesizer/src/process/tests.rs index 2ba5f76ab2..20e540cf4b 100644 --- a/synthesizer/src/process/tests.rs +++ b/synthesizer/src/process/tests.rs @@ -15,7 +15,7 @@ // along with the snarkVM library. If not, see . use super::*; -use crate::store::helpers::memory::FinalizeMemory; +use crate::{store::helpers::memory::FinalizeMemory, ProvingKeyId}; use circuit::network::AleoV0; use console::{ account::{Address, PrivateKey, ViewKey}, @@ -81,11 +81,22 @@ fn test_process_execute_mint() { assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(1, candidate.len()); assert_eq!(r2, candidate[0]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // use circuit::Environment; // @@ -227,14 +238,25 @@ fn test_process_multirecords() { assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(3, candidate.len()); assert_eq!(output_a, candidate[0]); assert_eq!(output_b, candidate[1]); assert_eq!(output_c, candidate[2]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // use circuit::Environment; // @@ -310,12 +332,23 @@ fn test_process_self_caller() { assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(1, candidate.len()); assert_eq!(output, candidate[0]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); } #[test] @@ -372,12 +405,23 @@ fn test_process_program_id() { assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(1, candidate.len()); assert_eq!(output, candidate[0]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); } #[test] @@ -413,13 +457,24 @@ fn test_process_output_operand() { assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = - process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = + ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(1, candidate.len()); assert_eq!(output, candidate[0]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); } // Initialize a new program. @@ -577,7 +632,18 @@ function compute: assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(4, candidate.len()); assert_eq!(r3, candidate[0]); @@ -585,7 +651,7 @@ function compute: assert_eq!(r5, candidate[2]); assert_eq!(r6, candidate[3]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // use circuit::Environment; // @@ -729,13 +795,24 @@ function transfer: assert_eq!(authorization.len(), 5); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(2, candidate.len()); assert_eq!(output_a, candidate[0]); assert_eq!(output_b, candidate[1]); - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // use circuit::Environment; // @@ -836,12 +913,23 @@ finalize compute: assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(0, candidate.len()); // Verify the execution. - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // Now, finalize the execution. process.finalize_execution(&store, &execution).unwrap(); @@ -939,12 +1027,23 @@ finalize compute: assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(0, candidate.len()); // Verify the execution. - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // Now, finalize the execution. process.finalize_execution(&store, &execution).unwrap(); @@ -1060,12 +1159,23 @@ finalize mint_public: assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(0, candidate.len()); // Verify the execution. - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // Now, finalize the execution. process.finalize_execution(&store, &execution).unwrap(); @@ -1202,12 +1312,23 @@ function mint: assert_eq!(authorization.len(), 2); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(0, candidate.len()); // Verify the execution. - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // Now, finalize the execution. process.finalize_execution(&store, &execution).unwrap(); @@ -1307,12 +1428,23 @@ finalize compute: assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(0, candidate.len()); // Verify the execution. - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // Now, finalize the execution. process.finalize_execution(&store, &execution).unwrap(); @@ -1418,12 +1550,23 @@ finalize compute: assert_eq!(authorization.len(), 1); // Execute the request. - let (response, execution, _inclusion, _metrics) = process.execute::(authorization, rng).unwrap(); + let (_response, mut execution, _inclusion, _metrics, mut function_assignments) = + process.prepare_function::(authorization).unwrap(); + assert_eq!(function_assignments.len(), execution.transitions().len()); + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + process.execute::(&mut execution, transition_assignments, None, rng).unwrap(); let candidate = response.outputs(); assert_eq!(0, candidate.len()); // Verify the execution. - process.verify_execution::(&execution).unwrap(); + process.verify_execution(&execution).unwrap(); // Now, finalize the execution. process.finalize_execution(&store, &execution).unwrap(); diff --git a/synthesizer/src/program/import/mod.rs b/synthesizer/src/program/import/mod.rs index a2b46374cf..4b4f36d68d 100644 --- a/synthesizer/src/program/import/mod.rs +++ b/synthesizer/src/program/import/mod.rs @@ -94,23 +94,23 @@ mod tests { let import4 = Import::::from_str("import foo.aleo;")?; assert_eq!(import1.partial_cmp(&import1), Some(Ordering::Equal)); - assert_eq!(import1.partial_cmp(&import2), Some(Ordering::Less)); + assert_eq!(import1.partial_cmp(&import2), Some(Ordering::Greater)); assert_eq!(import1.partial_cmp(&import3), Some(Ordering::Equal)); - assert_eq!(import1.partial_cmp(&import4), Some(Ordering::Less)); + assert_eq!(import1.partial_cmp(&import4), Some(Ordering::Greater)); - assert_eq!(import2.partial_cmp(&import1), Some(Ordering::Greater)); + assert_eq!(import2.partial_cmp(&import1), Some(Ordering::Less)); assert_eq!(import2.partial_cmp(&import2), Some(Ordering::Equal)); - assert_eq!(import2.partial_cmp(&import3), Some(Ordering::Greater)); + assert_eq!(import2.partial_cmp(&import3), Some(Ordering::Less)); assert_eq!(import2.partial_cmp(&import4), Some(Ordering::Equal)); assert_eq!(import3.partial_cmp(&import1), Some(Ordering::Equal)); - assert_eq!(import3.partial_cmp(&import2), Some(Ordering::Less)); + assert_eq!(import3.partial_cmp(&import2), Some(Ordering::Greater)); assert_eq!(import3.partial_cmp(&import3), Some(Ordering::Equal)); - assert_eq!(import3.partial_cmp(&import4), Some(Ordering::Less)); + assert_eq!(import3.partial_cmp(&import4), Some(Ordering::Greater)); - assert_eq!(import4.partial_cmp(&import1), Some(Ordering::Greater)); + assert_eq!(import4.partial_cmp(&import1), Some(Ordering::Less)); assert_eq!(import4.partial_cmp(&import2), Some(Ordering::Equal)); - assert_eq!(import4.partial_cmp(&import3), Some(Ordering::Greater)); + assert_eq!(import4.partial_cmp(&import3), Some(Ordering::Less)); assert_eq!(import4.partial_cmp(&import4), Some(Ordering::Equal)); Ok(()) diff --git a/synthesizer/src/program/instruction/operation/call.rs b/synthesizer/src/program/instruction/operation/call.rs index 7cad787631..819cf42ad0 100644 --- a/synthesizer/src/program/instruction/operation/call.rs +++ b/synthesizer/src/program/instruction/operation/call.rs @@ -335,7 +335,7 @@ impl Call { authorization.push(request.clone()); // Execute the request. - let response = substack.execute_function::(call_stack, rng)?; + let response = substack.execute_function::(call_stack)?; // Return the request and response. (request, response) @@ -357,7 +357,7 @@ impl Call { call_stack.push(request.clone())?; // Execute the request. - let response = substack.execute_function::(call_stack, rng)?; + let response = substack.execute_function::(call_stack)?; // Return the request and response. (request, response) } @@ -366,7 +366,7 @@ impl Call { bail!("Cannot 'execute' a function in 'evaluate' mode.") } // If the circuit is in execute mode, then evaluate and execute the instructions. - CallStack::Execute(authorization, ..) => { + CallStack::Prepare(authorization, ..) => { // Retrieve the next request (without popping it). let request = authorization.peek_next()?; // Ensure the inputs match the original inputs. @@ -378,7 +378,8 @@ impl Call { // Evaluate the function, and load the outputs. let console_response = substack.evaluate_function::(registers.call_stack().replicate())?; // Execute the request. - let response = substack.execute_function::(registers.call_stack(), rng)?; + let response = substack.execute_function::(registers.call_stack())?; + // Ensure the values are equal. if console_response.outputs() != response.outputs() { #[cfg(debug_assertions)] diff --git a/synthesizer/src/program/mod.rs b/synthesizer/src/program/mod.rs index f6e991c833..576a974522 100644 --- a/synthesizer/src/program/mod.rs +++ b/synthesizer/src/program/mod.rs @@ -629,7 +629,7 @@ impl TypeName for Program { #[cfg(test)] mod tests { use super::*; - use crate::{CallStack, Execution, Inclusion, StackEvaluate, StackExecute}; + use crate::{Assignments, CallStack, Execution, Inclusion, StackEvaluate, StackExecute}; use circuit::network::AleoV0; use console::{ account::{Address, PrivateKey}, @@ -1109,8 +1109,9 @@ function compute: let execution = Arc::new(RwLock::new(Execution::new())); let inclusion = Arc::new(RwLock::new(Inclusion::new())); let metrics = Arc::new(RwLock::new(Vec::new())); - let call_stack = CallStack::execute(authorization, execution, inclusion, metrics).unwrap(); - let response = stack.execute_function::(call_stack, rng).unwrap(); + let assignments = Assignments::::default(); + let call_stack = CallStack::prepare(authorization, execution, inclusion, metrics, assignments).unwrap(); + let response = stack.execute_function::(call_stack).unwrap(); let candidate = response.outputs(); assert_eq!(3, candidate.len()); assert_eq!(r2, candidate[0]); diff --git a/synthesizer/src/store/helpers/memory/transaction.rs b/synthesizer/src/store/helpers/memory/transaction.rs index 6532a2e50c..f2028ace82 100644 --- a/synthesizer/src/store/helpers/memory/transaction.rs +++ b/synthesizer/src/store/helpers/memory/transaction.rs @@ -185,8 +185,8 @@ pub struct ExecutionMemory { id_map: MemoryMap, bool)>, /// The reverse ID map. reverse_id_map: MemoryMap, - /// The inclusion map. - inclusion_map: MemoryMap>)>, + /// The execution map. + execution_map: MemoryMap>)>, /// The fee store. fee_store: FeeStore>, } @@ -195,7 +195,7 @@ pub struct ExecutionMemory { impl ExecutionStorage for ExecutionMemory { type IDMap = MemoryMap, bool)>; type ReverseIDMap = MemoryMap; - type InclusionMap = MemoryMap>)>; + type ExecutionMap = MemoryMap>)>; type FeeStorage = FeeMemory; /// Initializes the execution storage. @@ -203,7 +203,7 @@ impl ExecutionStorage for ExecutionMemory { Ok(Self { id_map: MemoryMap::default(), reverse_id_map: MemoryMap::default(), - inclusion_map: MemoryMap::default(), + execution_map: MemoryMap::default(), fee_store }) } @@ -218,9 +218,9 @@ impl ExecutionStorage for ExecutionMemory { &self.reverse_id_map } - /// Returns the inclusion map. - fn inclusion_map(&self) -> &Self::InclusionMap { - &self.inclusion_map + /// Returns the execution map. + fn execution_map(&self) -> &Self::ExecutionMap { + &self.execution_map } /// Returns the fee store. diff --git a/synthesizer/src/store/helpers/memory/transition.rs b/synthesizer/src/store/helpers/memory/transition.rs index dd2f991fc4..b1a5cbdce8 100644 --- a/synthesizer/src/store/helpers/memory/transition.rs +++ b/synthesizer/src/store/helpers/memory/transition.rs @@ -14,9 +14,13 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -use crate::{ - snark::Proof, - store::{helpers::memory::MemoryMap, InputStorage, InputStore, OutputStorage, OutputStore, TransitionStorage}, +use crate::store::{ + helpers::memory::MemoryMap, + InputStorage, + InputStore, + OutputStorage, + OutputStore, + TransitionStorage, }; use console::{ prelude::*, @@ -35,8 +39,6 @@ pub struct TransitionMemory { output_store: OutputStore>, /// The transition finalize inputs. finalize_map: MemoryMap>>>, - /// The transition proofs. - proof_map: MemoryMap>, /// The transition public keys. tpk_map: MemoryMap>, /// The reverse `tpk` map. @@ -53,7 +55,6 @@ impl TransitionStorage for TransitionMemory { type InputStorage = InputMemory; type OutputStorage = OutputMemory; type FinalizeMap = MemoryMap>>>; - type ProofMap = MemoryMap>; type TPKMap = MemoryMap>; type ReverseTPKMap = MemoryMap, N::TransitionID>; type TCMMap = MemoryMap>; @@ -66,7 +67,6 @@ impl TransitionStorage for TransitionMemory { input_store: InputStore::open(dev)?, output_store: OutputStore::open(dev)?, finalize_map: MemoryMap::default(), - proof_map: MemoryMap::default(), tpk_map: MemoryMap::default(), reverse_tpk_map: MemoryMap::default(), tcm_map: MemoryMap::default(), @@ -94,11 +94,6 @@ impl TransitionStorage for TransitionMemory { &self.finalize_map } - /// Returns the transition proofs. - fn proof_map(&self) -> &Self::ProofMap { - &self.proof_map - } - /// Returns the transition public keys. fn tpk_map(&self) -> &Self::TPKMap { &self.tpk_map diff --git a/synthesizer/src/store/helpers/rocksdb/internal/id.rs b/synthesizer/src/store/helpers/rocksdb/internal/id.rs index c77a7eb486..359e45fee0 100644 --- a/synthesizer/src/store/helpers/rocksdb/internal/id.rs +++ b/synthesizer/src/store/helpers/rocksdb/internal/id.rs @@ -80,7 +80,7 @@ pub enum DeploymentMap { pub enum ExecutionMap { ID = DataID::ExecutionIDMap as u16, ReverseID = DataID::ExecutionReverseIDMap as u16, - Inclusion = DataID::ExecutionInclusionMap as u16, + Execution = DataID::ExecutionExecutionMap as u16, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -127,7 +127,6 @@ pub enum TransactionMap { pub enum TransitionMap { Locator = DataID::TransitionLocatorMap as u16, Finalize = DataID::TransitionFinalizeMap as u16, - Proof = DataID::TransitionProofMap as u16, TPK = DataID::TransitionTPKMap as u16, ReverseTPK = DataID::TransitionReverseTPKMap as u16, TCM = DataID::TransitionTCMMap as u16, @@ -177,7 +176,7 @@ enum DataID { // Execution ExecutionIDMap, ExecutionReverseIDMap, - ExecutionInclusionMap, + ExecutionExecutionMap, // Fee FeeFeeMap, FeeReverseFeeMap, @@ -204,7 +203,6 @@ enum DataID { // Transition TransitionLocatorMap, TransitionFinalizeMap, - TransitionProofMap, TransitionTPKMap, TransitionReverseTPKMap, TransitionTCMMap, diff --git a/synthesizer/src/store/helpers/rocksdb/transaction.rs b/synthesizer/src/store/helpers/rocksdb/transaction.rs index 8eafa7e4a8..79f14d46a6 100644 --- a/synthesizer/src/store/helpers/rocksdb/transaction.rs +++ b/synthesizer/src/store/helpers/rocksdb/transaction.rs @@ -197,8 +197,8 @@ pub struct ExecutionDB { id_map: DataMap, bool)>, /// The reverse ID map. reverse_id_map: DataMap, - /// The inclusion map. - inclusion_map: DataMap>)>, + /// The execution map. + execution_map: DataMap>)>, /// The fee store. fee_store: FeeStore>, } @@ -207,7 +207,7 @@ pub struct ExecutionDB { impl ExecutionStorage for ExecutionDB { type IDMap = DataMap, bool)>; type ReverseIDMap = DataMap; - type InclusionMap = DataMap>)>; + type ExecutionMap = DataMap>)>; type FeeStorage = FeeDB; /// Initializes the execution storage. @@ -217,7 +217,7 @@ impl ExecutionStorage for ExecutionDB { Ok(Self { id_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Execution(ExecutionMap::ID))?, reverse_id_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Execution(ExecutionMap::ReverseID))?, - inclusion_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Execution(ExecutionMap::Inclusion))?, + execution_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Execution(ExecutionMap::Execution))?, fee_store, }) } @@ -232,9 +232,9 @@ impl ExecutionStorage for ExecutionDB { &self.reverse_id_map } - /// Returns the inclusion map. - fn inclusion_map(&self) -> &Self::InclusionMap { - &self.inclusion_map + /// Returns the execution map. + fn execution_map(&self) -> &Self::ExecutionMap { + &self.execution_map } /// Returns the fee store. diff --git a/synthesizer/src/store/helpers/rocksdb/transition.rs b/synthesizer/src/store/helpers/rocksdb/transition.rs index 53fca76562..1b105f57c3 100644 --- a/synthesizer/src/store/helpers/rocksdb/transition.rs +++ b/synthesizer/src/store/helpers/rocksdb/transition.rs @@ -14,16 +14,13 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -use crate::{ - snark::Proof, - store::{ - helpers::rocksdb::{self, DataMap, Database, MapID, TransitionInputMap, TransitionMap, TransitionOutputMap}, - InputStorage, - InputStore, - OutputStorage, - OutputStore, - TransitionStorage, - }, +use crate::store::{ + helpers::rocksdb::{self, DataMap, Database, MapID, TransitionInputMap, TransitionMap, TransitionOutputMap}, + InputStorage, + InputStore, + OutputStorage, + OutputStore, + TransitionStorage, }; use console::{ prelude::*, @@ -40,8 +37,6 @@ pub struct TransitionDB { input_store: InputStore>, /// The transition output store. output_store: OutputStore>, - /// The transition proofs. - proof_map: DataMap>, /// The transition finalize inputs. finalize_map: DataMap>>>, /// The transition public keys. @@ -60,7 +55,6 @@ impl TransitionStorage for TransitionDB { type InputStorage = InputDB; type OutputStorage = OutputDB; type FinalizeMap = DataMap>>>; - type ProofMap = DataMap>; type TPKMap = DataMap>; type ReverseTPKMap = DataMap, N::TransitionID>; type TCMMap = DataMap>; @@ -73,7 +67,6 @@ impl TransitionStorage for TransitionDB { input_store: InputStore::open(dev)?, output_store: OutputStore::open(dev)?, finalize_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Transition(TransitionMap::Finalize))?, - proof_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Transition(TransitionMap::Proof))?, tpk_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Transition(TransitionMap::TPK))?, reverse_tpk_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Transition(TransitionMap::ReverseTPK))?, tcm_map: rocksdb::RocksDB::open_map(N::ID, dev, MapID::Transition(TransitionMap::TCM))?, @@ -101,11 +94,6 @@ impl TransitionStorage for TransitionDB { &self.finalize_map } - /// Returns the transition proofs. - fn proof_map(&self) -> &Self::ProofMap { - &self.proof_map - } - /// Returns the transition public keys. fn tpk_map(&self) -> &Self::TPKMap { &self.tpk_map diff --git a/synthesizer/src/store/transaction/execution.rs b/synthesizer/src/store/transaction/execution.rs index ed69e14cca..257c254987 100644 --- a/synthesizer/src/store/transaction/execution.rs +++ b/synthesizer/src/store/transaction/execution.rs @@ -39,9 +39,9 @@ pub trait ExecutionStorage: Clone + Send + Sync { type IDMap: for<'a> Map<'a, N::TransactionID, (Vec, bool)>; /// The mapping of `transition ID` to `transaction ID`. type ReverseIDMap: for<'a> Map<'a, N::TransitionID, N::TransactionID>; - /// The mapping of `transaction ID` to `(global state root, (optional) inclusion proof)`. - type InclusionMap: for<'a> Map<'a, N::TransactionID, (N::StateRoot, Option>)>; - /// The fee storage. + /// The mapping of `transaction ID` to `(global state root, (optional) proof)`. + type ExecutionMap: for<'a> Map<'a, N::TransactionID, (N::StateRoot, Option>)>; + /// The fee storage type FeeStorage: FeeStorage; /// Initializes the execution storage. @@ -51,8 +51,8 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn id_map(&self) -> &Self::IDMap; /// Returns the reverse ID map. fn reverse_id_map(&self) -> &Self::ReverseIDMap; - /// Returns the inclusion map. - fn inclusion_map(&self) -> &Self::InclusionMap; + /// Returns the execution map. + fn execution_map(&self) -> &Self::ExecutionMap; /// Returns the fee store. fn fee_store(&self) -> &FeeStore; /// Returns the transition store. @@ -69,7 +69,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn start_atomic(&self) { self.id_map().start_atomic(); self.reverse_id_map().start_atomic(); - self.inclusion_map().start_atomic(); + self.execution_map().start_atomic(); self.fee_store().start_atomic(); } @@ -77,7 +77,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn is_atomic_in_progress(&self) -> bool { self.id_map().is_atomic_in_progress() || self.reverse_id_map().is_atomic_in_progress() - || self.inclusion_map().is_atomic_in_progress() + || self.execution_map().is_atomic_in_progress() || self.fee_store().is_atomic_in_progress() } @@ -85,7 +85,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn atomic_checkpoint(&self) { self.id_map().atomic_checkpoint(); self.reverse_id_map().atomic_checkpoint(); - self.inclusion_map().atomic_checkpoint(); + self.execution_map().atomic_checkpoint(); self.fee_store().atomic_checkpoint(); } @@ -93,7 +93,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn atomic_rewind(&self) { self.id_map().atomic_rewind(); self.reverse_id_map().atomic_rewind(); - self.inclusion_map().atomic_rewind(); + self.execution_map().atomic_rewind(); self.fee_store().atomic_rewind(); } @@ -101,7 +101,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn abort_atomic(&self) { self.id_map().abort_atomic(); self.reverse_id_map().abort_atomic(); - self.inclusion_map().abort_atomic(); + self.execution_map().abort_atomic(); self.fee_store().abort_atomic(); } @@ -109,7 +109,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { fn finish_atomic(&self) -> Result<()> { self.id_map().finish_atomic()?; self.reverse_id_map().finish_atomic()?; - self.inclusion_map().finish_atomic()?; + self.execution_map().finish_atomic()?; self.fee_store().finish_atomic() } @@ -128,8 +128,8 @@ pub trait ExecutionStorage: Clone + Send + Sync { let transition_ids = execution.transitions().map(Transition::id).copied().collect(); // Retrieve the global state root. let global_state_root = execution.global_state_root(); - // Retrieve the inclusion proof. - let inclusion_proof = execution.inclusion_proof().cloned(); + // Retrieve the proof. + let proof = execution.proof().cloned(); atomic_batch_scope!(self, { // Store the transition IDs. @@ -143,8 +143,8 @@ pub trait ExecutionStorage: Clone + Send + Sync { self.transition_store().insert(transition)?; } - // Store the global state root and inclusion proof. - self.inclusion_map().insert(*transaction_id, (global_state_root, inclusion_proof))?; + // Store the global state root and proof. + self.execution_map().insert(*transaction_id, (global_state_root, proof))?; // Store the fee. if let Some(fee) = fee { @@ -177,7 +177,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { } // Remove the global state root and inclusion proof. - self.inclusion_map().remove(transaction_id)?; + self.execution_map().remove(transaction_id)?; // Remove the fee. if has_fee { @@ -213,9 +213,9 @@ pub trait ExecutionStorage: Clone + Send + Sync { None => return Ok(None), }; - // Retrieve the global state root and inclusion proof. - let (global_state_root, inclusion_proof) = match self.inclusion_map().get_confirmed(transaction_id)? { - Some(inclusion) => cow_to_cloned!(inclusion), + // Retrieve the global state root and proof. + let (global_state_root, proof) = match self.execution_map().get_confirmed(transaction_id)? { + Some(execution) => cow_to_cloned!(execution), None => bail!("Failed to get the inclusion proof for the transaction '{transaction_id}'"), }; @@ -231,7 +231,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { } // Return the execution. - Ok(Some(Execution::from(transitions.into_iter(), global_state_root, inclusion_proof)?)) + Ok(Some(Execution::from(transitions.into_iter(), global_state_root, proof)?)) } /// Returns the transaction for the given `transaction ID`. @@ -242,9 +242,9 @@ pub trait ExecutionStorage: Clone + Send + Sync { None => return Ok(None), }; - // Retrieve the global state root and inclusion proof. - let (global_state_root, inclusion_proof) = match self.inclusion_map().get_confirmed(transaction_id)? { - Some(inclusion) => cow_to_cloned!(inclusion), + // Retrieve the global state root and proof. + let (global_state_root, proof) = match self.execution_map().get_confirmed(transaction_id)? { + Some(execution) => cow_to_cloned!(execution), None => bail!("Failed to get the inclusion proof for the transaction '{transaction_id}'"), }; @@ -260,7 +260,7 @@ pub trait ExecutionStorage: Clone + Send + Sync { } // Construct the execution. - let execution = Execution::from(transitions.into_iter(), global_state_root, inclusion_proof)?; + let execution = Execution::from(transitions.into_iter(), global_state_root, proof)?; // Construct the transaction. let transaction = match has_fee { diff --git a/synthesizer/src/store/transaction/fee.rs b/synthesizer/src/store/transaction/fee.rs index 21f578c54c..6222a5a108 100644 --- a/synthesizer/src/store/transaction/fee.rs +++ b/synthesizer/src/store/transaction/fee.rs @@ -33,7 +33,7 @@ use core::marker::PhantomData; /// A trait for fee storage. pub trait FeeStorage: Clone + Send + Sync { - /// The mapping of `transaction ID` to `(fee transition ID, global state root, inclusion proof)`. + /// The mapping of `transaction ID` to `(fee transition ID, global state root, proof)`. type FeeMap: for<'a> Map<'a, N::TransactionID, (N::TransitionID, N::StateRoot, Option>)>; /// The mapping of `fee transition ID` to `transaction ID`. type ReverseFeeMap: for<'a> Map<'a, N::TransitionID, N::TransactionID>; @@ -102,10 +102,8 @@ pub trait FeeStorage: Clone + Send + Sync { fn insert(&self, transaction_id: N::TransactionID, fee: &Fee) -> Result<()> { atomic_batch_scope!(self, { // Store the fee. - self.fee_map().insert( - transaction_id, - (*fee.transition_id(), fee.global_state_root(), fee.inclusion_proof().cloned()), - )?; + self.fee_map() + .insert(transaction_id, (*fee.transition_id(), fee.global_state_root(), fee.proof().cloned()))?; self.reverse_fee_map().insert(*fee.transition_id(), transaction_id)?; // Store the fee transition. @@ -149,14 +147,13 @@ pub trait FeeStorage: Clone + Send + Sync { /// Returns the fee for the given `transaction ID`. fn get_fee(&self, transaction_id: &N::TransactionID) -> Result>> { // Retrieve the fee transition ID. - let (fee_transition_id, global_state_root, inclusion_proof) = - match self.fee_map().get_confirmed(transaction_id)? { - Some(fee) => cow_to_cloned!(fee), - None => return Ok(None), - }; + let (fee_transition_id, global_state_root, proof) = match self.fee_map().get_confirmed(transaction_id)? { + Some(fee) => cow_to_cloned!(fee), + None => return Ok(None), + }; // Retrieve the fee transition. match self.transition_store().get_transition(&fee_transition_id)? { - Some(transition) => Ok(Some(Fee::from(transition, global_state_root, inclusion_proof))), + Some(transition) => Ok(Some(Fee::from(transition, global_state_root, proof))), None => bail!("Failed to locate the fee transition for transaction '{transaction_id}'"), } } diff --git a/synthesizer/src/store/transition/mod.rs b/synthesizer/src/store/transition/mod.rs index 8178ece84f..9b41a2ed09 100644 --- a/synthesizer/src/store/transition/mod.rs +++ b/synthesizer/src/store/transition/mod.rs @@ -25,7 +25,6 @@ use crate::{ block::{Input, Output, Transition}, cow_to_cloned, cow_to_copied, - snark::Proof, store::helpers::{Map, MapRead}, }; use console::{ @@ -47,8 +46,6 @@ pub trait TransitionStorage: Clone + Send + Sync { type OutputStorage: OutputStorage; /// The transition finalize inputs. type FinalizeMap: for<'a> Map<'a, N::TransitionID, Option>>>; - /// The transition proofs. - type ProofMap: for<'a> Map<'a, N::TransitionID, Proof>; /// The transition public keys. type TPKMap: for<'a> Map<'a, N::TransitionID, Group>; /// The mapping of `transition public key` to `transition ID`. @@ -69,8 +66,6 @@ pub trait TransitionStorage: Clone + Send + Sync { fn output_store(&self) -> &OutputStore; /// Returns the transition finalize inputs map. fn finalize_map(&self) -> &Self::FinalizeMap; - /// Returns the transition proofs map. - fn proof_map(&self) -> &Self::ProofMap; /// Returns the transition public keys map. fn tpk_map(&self) -> &Self::TPKMap; /// Returns the reverse `tpk` map. @@ -92,7 +87,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.input_store().start_atomic(); self.output_store().start_atomic(); self.finalize_map().start_atomic(); - self.proof_map().start_atomic(); self.tpk_map().start_atomic(); self.reverse_tpk_map().start_atomic(); self.tcm_map().start_atomic(); @@ -105,7 +99,6 @@ pub trait TransitionStorage: Clone + Send + Sync { || self.input_store().is_atomic_in_progress() || self.output_store().is_atomic_in_progress() || self.finalize_map().is_atomic_in_progress() - || self.proof_map().is_atomic_in_progress() || self.tpk_map().is_atomic_in_progress() || self.reverse_tpk_map().is_atomic_in_progress() || self.tcm_map().is_atomic_in_progress() @@ -118,7 +111,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.input_store().atomic_checkpoint(); self.output_store().atomic_checkpoint(); self.finalize_map().atomic_checkpoint(); - self.proof_map().atomic_checkpoint(); self.tpk_map().atomic_checkpoint(); self.reverse_tpk_map().atomic_checkpoint(); self.tcm_map().atomic_checkpoint(); @@ -131,7 +123,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.input_store().atomic_rewind(); self.output_store().atomic_rewind(); self.finalize_map().atomic_rewind(); - self.proof_map().atomic_rewind(); self.tpk_map().atomic_rewind(); self.reverse_tpk_map().atomic_rewind(); self.tcm_map().atomic_rewind(); @@ -144,7 +135,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.input_store().abort_atomic(); self.output_store().abort_atomic(); self.finalize_map().abort_atomic(); - self.proof_map().abort_atomic(); self.tpk_map().abort_atomic(); self.reverse_tpk_map().abort_atomic(); self.tcm_map().abort_atomic(); @@ -157,7 +147,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.input_store().finish_atomic()?; self.output_store().finish_atomic()?; self.finalize_map().finish_atomic()?; - self.proof_map().finish_atomic()?; self.tpk_map().finish_atomic()?; self.reverse_tpk_map().finish_atomic()?; self.tcm_map().finish_atomic()?; @@ -177,8 +166,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.output_store().insert(transition_id, transition.outputs())?; // Store the finalize inputs. self.finalize_map().insert(transition_id, transition.finalize().cloned())?; - // Store the proof. - self.proof_map().insert(transition_id, transition.proof().clone())?; // Store `tpk`. self.tpk_map().insert(transition_id, *transition.tpk())?; // Store the reverse `tpk` entry. @@ -214,8 +201,6 @@ pub trait TransitionStorage: Clone + Send + Sync { self.output_store().remove(transition_id)?; // Remove the finalize inputs. self.finalize_map().remove(transition_id)?; - // Remove the proof. - self.proof_map().remove(transition_id)?; // Remove `tpk`. self.tpk_map().remove(transition_id)?; // Remove the reverse `tpk` entry. @@ -242,15 +227,13 @@ pub trait TransitionStorage: Clone + Send + Sync { let outputs = self.output_store().get_outputs(transition_id)?; // Retrieve the finalize inputs. let finalize = self.finalize_map().get_confirmed(transition_id)?; - // Retrieve the proof. - let proof = self.proof_map().get_confirmed(transition_id)?; // Retrieve `tpk`. let tpk = self.tpk_map().get_confirmed(transition_id)?; // Retrieve `tcm`. let tcm = self.tcm_map().get_confirmed(transition_id)?; - match (finalize, proof, tpk, tcm) { - (Some(finalize), Some(proof), Some(tpk), Some(tcm)) => { + match (finalize, tpk, tcm) { + (Some(finalize), Some(tpk), Some(tcm)) => { // Construct the transition. let transition = Transition::new( program_id, @@ -258,7 +241,6 @@ pub trait TransitionStorage: Clone + Send + Sync { inputs, outputs, cow_to_cloned!(finalize), - cow_to_cloned!(proof), cow_to_cloned!(tpk), cow_to_cloned!(tcm), )?; @@ -284,8 +266,6 @@ pub struct TransitionStore> { outputs: OutputStore, /// The map of transition finalize inputs. finalize: T::FinalizeMap, - /// The map of transition proofs. - proof: T::ProofMap, /// The map of transition public keys. tpk: T::TPKMap, /// The reverse `tpk` map. @@ -309,7 +289,6 @@ impl> TransitionStore { inputs: (*storage.input_store()).clone(), outputs: (*storage.output_store()).clone(), finalize: storage.finalize_map().clone(), - proof: storage.proof_map().clone(), tpk: storage.tpk_map().clone(), reverse_tpk: storage.reverse_tpk_map().clone(), tcm: storage.tcm_map().clone(), @@ -325,7 +304,6 @@ impl> TransitionStore { inputs: (*storage.input_store()).clone(), outputs: (*storage.output_store()).clone(), finalize: storage.finalize_map().clone(), - proof: storage.proof_map().clone(), tpk: storage.tpk_map().clone(), reverse_tpk: storage.reverse_tpk_map().clone(), tcm: storage.tcm_map().clone(), @@ -642,11 +620,6 @@ impl> TransitionStore { /* Metadata */ - /// Returns an iterator over the proofs, for all transitions. - pub fn proofs(&self) -> impl '_ + Iterator>> { - self.proof.values_confirmed() - } - /// Returns an iterator over the transition public keys, for all transitions. pub fn tpks(&self) -> impl '_ + Iterator>> { self.tpk.values_confirmed() diff --git a/synthesizer/src/vm/authorize.rs b/synthesizer/src/vm/authorize.rs index 3cef0efd25..b2396c2afd 100644 --- a/synthesizer/src/vm/authorize.rs +++ b/synthesizer/src/vm/authorize.rs @@ -67,6 +67,7 @@ impl> VM { Ok(cast_ref!(authorization as Authorization).clone()) }}; } + // Process the logic. process!(self, logic) } diff --git a/synthesizer/src/vm/execute.rs b/synthesizer/src/vm/execute.rs index 41705959d4..7e0ca6306d 100644 --- a/synthesizer/src/vm/execute.rs +++ b/synthesizer/src/vm/execute.rs @@ -15,6 +15,9 @@ // along with the snarkVM library. If not, see . use super::*; +use crate::ProvingKeyId; +use circuit::AleoV0; +use std::collections::BTreeMap; impl> VM { /// Returns a new execute transaction. @@ -89,36 +92,58 @@ impl> VM { let authorization = cast_ref!(authorization as Authorization<$network>); lap!(timer, "Prepare the authorization"); - // Execute the call. - let (response, execution, inclusion, metrics) = - $process.execute::<$aleo, _>(authorization.clone(), rng)?; - lap!(timer, "Execute the call"); - - // Prepare the assignments. - let (assignments, global_state_root) = { - let execution = cast_ref!(execution as Execution); + // Call prepare + let (response, mut execution, inclusion, metrics, mut function_assignments) = + $process.prepare_function::<$aleo>(authorization.clone())?; + lap!(timer, "Prepare the execution"); + + if function_assignments.len() != execution.transitions().len() { + bail!("expected to have as many function assignments as transitions"); + } + + let mut transition_assignments = BTreeMap::<_, Vec<_>>::new(); + for transition in execution.transitions() { + let pk_id = ProvingKeyId { + program_id: *transition.program_id(), + function_name: *transition.function_name(), + }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + + let (inclusion_assignments, global_state_root) = { + let execution = cast_ref!(&mut execution as Execution); let inclusion = cast_ref!(inclusion as Inclusion); - inclusion.prepare_execution(execution, query)? + inclusion.prepare_execution(&execution, query)? }; - let assignments = cast_ref!(assignments as Vec>); - let global_state_root = *cast_ref!((*global_state_root) as Field<$network>); lap!(timer, "Prepare the assignments"); - // Compute the inclusion proof and update the execution. - let execution = - inclusion.prove_execution::<$aleo, _>(execution, assignments, global_state_root.into(), rng)?; - lap!(timer, "Compute the inclusion proof"); + let inclusion_assignments = cast_ref!(inclusion_assignments as Vec>); + let inclusion_assignments = inclusion_assignments + .into_iter() + .map(|a| a.to_circuit_assignment::().unwrap()) + .collect_vec(); + let inclusion_assignments = if inclusion_assignments.len() == 0 { + None + } else { + // NOTE: the inclusion circuit will always be different from any transition circuit because + // in execute_function() we add some constraints unique to transitions + Some(inclusion_assignments) + }; + + // Execute the call. + $process.execute::<$aleo, _>(&mut execution, transition_assignments, inclusion_assignments, rng)?; - // Prepare the return. let response = cast_ref!(response as Response).clone(); - let execution = cast_ref!(execution as Execution).clone(); + let mut execution = cast_ref!(execution as Execution).clone(); + execution.update_global_state_root(global_state_root); let metrics = cast_ref!(metrics as Vec>).clone(); - lap!(timer, "Prepare the response and execution"); - finish!(timer); + lap!(timer, "Execute the call"); - // Return the response, execution, and metrics. Ok((response, execution, metrics)) }}; } @@ -135,6 +160,7 @@ mod tests { account::{Address, ViewKey}, network::Testnet3, program::{Ciphertext, Value}, + types::Field, }; use indexmap::IndexMap; @@ -187,13 +213,13 @@ mod tests { // Assert the size of the transaction. let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len(); - assert_eq!(1387, transaction_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(1388, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. assert!(matches!(transaction, Transaction::Execute(_, _, _))); if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); - assert_eq!(1352, execution_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(1353, execution_size_in_bytes, "Update me if serialization has changed"); } } @@ -226,13 +252,13 @@ mod tests { // Assert the size of the transaction. let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len(); - assert_eq!(2595, transaction_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(2215, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. assert!(matches!(transaction, Transaction::Execute(_, _, _))); if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); - assert_eq!(2560, execution_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(2180, execution_size_in_bytes, "Update me if serialization has changed"); } } @@ -260,13 +286,13 @@ mod tests { // Assert the size of the transaction. let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len(); - assert_eq!(2480, transaction_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(2100, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. assert!(matches!(transaction, Transaction::Execute(_, _, _))); if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); - assert_eq!(2445, execution_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(2065, execution_size_in_bytes, "Update me if serialization has changed"); } } @@ -293,13 +319,13 @@ mod tests { // Assert the size of the transaction. let transaction_size_in_bytes = transaction.to_bytes_le().unwrap().len(); - assert_eq!(2492, transaction_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(2112, transaction_size_in_bytes, "Update me if serialization has changed"); // Assert the size of the execution. assert!(matches!(transaction, Transaction::Execute(_, _, _))); if let Transaction::Execute(_, execution, _) = &transaction { let execution_size_in_bytes = execution.to_bytes_le().unwrap().len(); - assert_eq!(2457, execution_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(2077, execution_size_in_bytes, "Update me if serialization has changed"); } } } diff --git a/synthesizer/src/vm/execute_fee.rs b/synthesizer/src/vm/execute_fee.rs index 64d894994a..624dadc396 100644 --- a/synthesizer/src/vm/execute_fee.rs +++ b/synthesizer/src/vm/execute_fee.rs @@ -16,6 +16,8 @@ use super::*; +use circuit::AleoV0; + impl> VM { /// Executes a fee for the given private key, fee record, and fee amount (in microcredits). /// Returns the fee transaction. @@ -76,30 +78,43 @@ impl> VM { lap!(timer, "Prepare the private key and fee record"); // Execute the call to fee. - let (response, fee_transition, inclusion, metrics) = - $process.execute_fee::<$aleo, _>(private_key, fee_record.clone(), fee_in_microcredits, rng)?; + let (response, fee_transition, inclusion, mut fee_assignments, metrics) = + $process.prepare_fee::<$aleo, _>(private_key, fee_record.clone(), fee_in_microcredits, rng)?; lap!(timer, "Execute the call to fee"); // Prepare the assignments. - let assignments = { + let inclusion_assignments = { let fee_transition = cast_ref!(fee_transition as Transition); let inclusion = cast_ref!(inclusion as Inclusion); inclusion.prepare_fee(fee_transition, query)? }; - let assignments = cast_ref!(assignments as Vec>); + let inclusion_assignments = cast_ref!(inclusion_assignments as Vec>); + + let global_state_root = Inclusion::fee_global_state_root(inclusion_assignments)?; + + let inclusion_assignments = inclusion_assignments + .into_iter() + .map(|ia| ia.to_circuit_assignment::().unwrap()) + .collect_vec(); + let inclusion_assignments = + if inclusion_assignments.len() == 0 { None } else { Some(inclusion_assignments) }; lap!(timer, "Prepare the assignments"); - // Compute the inclusion proof and construct the fee. - let fee = inclusion.prove_fee::<$aleo, _>(fee_transition, assignments, rng)?; - lap!(timer, "Compute the inclusion proof and construct the fee"); + let mut fee = Fee::from(fee_transition, global_state_root, None); + + // Execute the call. + $process.execute_fee::<$aleo, _>( + &mut fee, + fee_assignments.make_contiguous(), + inclusion_assignments, + rng, + )?; + lap!(timer, "Execute the call"); // Prepare the return. let response = cast_ref!(response as Response).clone(); let fee = cast_ref!(fee as Fee).clone(); let metrics = cast_ref!(metrics as Vec>).clone(); - lap!(timer, "Prepare the response, fee, and metrics"); - - finish!(timer); // Return the response, fee, metrics. Ok((response, fee, metrics)) @@ -115,6 +130,8 @@ mod tests { use super::*; use crate::store::helpers::memory::ConsensusMemory; use console::{account::ViewKey, network::Testnet3, program::Ciphertext}; + // use snarkvm_fields::Field; + use console::types::Field; use indexmap::IndexMap; @@ -162,6 +179,6 @@ mod tests { // Assert the size of the transition. let fee_size_in_bytes = fee.to_bytes_le().unwrap().len(); - assert_eq!(2247, fee_size_in_bytes, "Update me if serialization has changed"); + assert_eq!(1867, fee_size_in_bytes, "Update me if serialization has changed"); } } diff --git a/synthesizer/src/vm/helpers/macros.rs b/synthesizer/src/vm/helpers/macros.rs index 0e0947e95c..032fdc3299 100644 --- a/synthesizer/src/vm/helpers/macros.rs +++ b/synthesizer/src/vm/helpers/macros.rs @@ -23,6 +23,12 @@ macro_rules! cast_ref { .downcast_ref::<$object<$($traits),+>>() .ok_or_else(|| anyhow!("Failed to downcast {}", stringify!($variable)))? }}; + // Example: cast_ref!(&mut bar as Bar) + (&mut $variable:ident as $object:ident<$($traits:path),+>) => {{ + (&mut $variable as &mut dyn std::any::Any) + .downcast_mut::<$object<$($traits),+>>() + .ok_or_else(|| anyhow!("Failed to downcast {}", stringify!($variable)))? + }}; // Example: cast_ref!(bar as Bar) ($variable:ident as $object:ident<$($traits:path),+>) => {{ (&$variable as &dyn std::any::Any) diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index 0ae5d27eba..e714ecc830 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -39,7 +39,6 @@ use console::{ account::{Address, PrivateKey}, network::prelude::*, program::{Entry, Identifier, Literal, Plaintext, ProgramID, ProgramOwner, Record, Response, Value}, - types::Field, }; use aleo_std::prelude::{finish, lap, timer}; @@ -189,20 +188,12 @@ impl> VM { #[cfg(test)] pub(crate) mod test_helpers { use super::*; - use crate::{ - program::Program, - store::helpers::memory::ConsensusMemory, - Block, - Fee, - Header, - Inclusion, - Metadata, - Transition, - }; + use crate::{program::Program, store::helpers::memory::ConsensusMemory, Block, Fee, Header, Metadata, Transition}; use console::{ account::{Address, ViewKey}, network::Testnet3, program::Value, + types::Field, }; use indexmap::IndexMap; @@ -393,9 +384,11 @@ function compute: let records = genesis.transitions().cloned().flat_map(Transition::into_records).collect::>(); trace!("Unspent Records:\n{:#?}", records); + let mut records_iter = records.values(); // Select a record to spend. - let record = records.values().next().unwrap().decrypt(&caller_view_key).unwrap(); + let record = records_iter.next().unwrap().decrypt(&caller_view_key).unwrap(); + let record_for_fee = records_iter.next().unwrap().decrypt(&caller_view_key).unwrap(); // Initialize the VM. let vm = sample_vm(); @@ -404,17 +397,18 @@ function compute: // Prepare the inputs. let inputs = [ + Value::::Record(record), Value::::from_str(&address.to_string()).unwrap(), Value::::from_str("1u64").unwrap(), ] .into_iter(); // Authorize. - let authorization = vm.authorize(&caller_private_key, "credits.aleo", "mint", inputs, rng).unwrap(); + let authorization = vm.authorize(&caller_private_key, "credits.aleo", "transfer", inputs, rng).unwrap(); assert_eq!(authorization.len(), 1); // Execute the fee. - let fee = vm.execute_fee_raw(&caller_private_key, record, 100, None, rng).unwrap().1; + let fee = vm.execute_fee_raw(&caller_private_key, record_for_fee, 100, None, rng).unwrap().1; // Execute. let transaction = vm.execute_authorization(authorization, Some(fee), None, rng).unwrap(); @@ -455,7 +449,6 @@ function compute: vm.execute_fee_raw(&caller_private_key, record, 1u64, None, rng).unwrap(); // Verify. assert!(vm.verify_fee(&fee)); - assert!(Inclusion::verify_fee(&fee).is_ok()); // Return the fee. fee }) diff --git a/synthesizer/src/vm/verify.rs b/synthesizer/src/vm/verify.rs index c4b5c283a3..501a58c172 100644 --- a/synthesizer/src/vm/verify.rs +++ b/synthesizer/src/vm/verify.rs @@ -190,7 +190,7 @@ impl> VM { let timer = timer!("VM::verify_execution"); // Verify the execution. - let verification = self.process.read().verify_execution::(execution); + let verification = self.process.read().verify_execution(execution); finish!(timer); match verification { @@ -229,7 +229,7 @@ impl> VM { mod tests { use super::*; - use crate::{Block, Header, Inclusion, Metadata, Transaction}; + use crate::{Block, Header, Metadata, Transaction}; use console::{ account::{Address, ViewKey}, types::Field, @@ -287,11 +287,9 @@ mod tests { match transaction { Transaction::Execute(_, execution, _) => { - // Ensure the inclusion proof *does not* exist. - assert!(execution.inclusion_proof().is_none()); // This is 'None', because this execution called 'credits.aleo/mint'. - // Verify the inclusion. - assert!(Inclusion::verify_execution(&execution).is_ok()); - // Verify the execution. + // Ensure the inclusion proof exists. + assert!(execution.proves_inclusion()); // This is true, because this execution called 'credits.aleo/transfer'. + // Verify the execution and inclusion. assert!(vm.check_execution(&execution).is_ok()); assert!(vm.verify_execution(&execution)); @@ -316,11 +314,10 @@ mod tests { match transaction { Transaction::Execute(_, _, Some(fee)) => { - // Ensure the inclusion proof exists. - assert!(fee.inclusion_proof().is_some()); - // Verify the inclusion. - assert!(Inclusion::verify_fee(&fee).is_ok()); - // Verify the fee. + // Ensure the proof exists. + assert!(fee.proof().is_some()); + assert!(fee.proves_inclusion()); // This is true, because this execution called 'credits.aleo/transfer'. + // Verify the fee and inclusion. assert!(vm.check_fee(&fee).is_ok()); assert!(vm.verify_fee(&fee)); diff --git a/vm/package/run.rs b/vm/package/run.rs index 53102b527f..da4fbcddf8 100644 --- a/vm/package/run.rs +++ b/vm/package/run.rs @@ -15,7 +15,8 @@ // along with the snarkVM library. If not, see . use super::*; -use snarkvm_synthesizer::CallMetrics; +use snarkvm_synthesizer::{CallMetrics, ProvingKeyId}; +use std::collections::BTreeMap; impl Package { /// Runs a program function with the given inputs. @@ -96,8 +97,23 @@ impl Package { // Adds the verifying key to the process. process.insert_verifying_key(program_id, &function_name, verifier.verifying_key().clone())?; + let (response, mut execution, inclusion, metrics, mut function_assignments) = + process.prepare_function::(authorization)?; + if function_assignments.len() != execution.transitions().len() { + bail!("expected to have as many function_assignments as transitions"); + } + let mut transition_assignments: BTreeMap, Vec<_>> = BTreeMap::new(); + for transition in execution.transitions() { + let pk_id = + ProvingKeyId { program_id: *transition.program_id(), function_name: *transition.function_name() }; + transition_assignments + .entry(pk_id) + .and_modify(|assignments| assignments.push(function_assignments.pop_front().unwrap())) + .or_insert(vec![function_assignments.pop_front().unwrap()]); + } + // Execute the circuit. - let (response, execution, inclusion, metrics) = process.execute::(authorization, rng)?; + process.execute::(&mut execution, transition_assignments, None, rng)?; Ok((response, execution, inclusion, metrics)) }