diff --git a/taiga_halo2/benches/action_proof.rs b/taiga_halo2/benches/action_proof.rs index 436d4f8d..28a6fe7b 100644 --- a/taiga_halo2/benches/action_proof.rs +++ b/taiga_halo2/benches/action_proof.rs @@ -66,8 +66,8 @@ fn bench_action_proof(name: &str, c: &mut Criterion) { } }; let input_merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); - let rcv = pallas::Scalar::random(&mut rng); - ActionInfo::new(input_note, input_merkle_path, output_note, rcv) + let rseed = RandomSeed::random(&mut rng); + ActionInfo::new(input_note, input_merkle_path, output_note, rseed) }; let (action, action_circuit) = action_info.build(); let params = SETUP_PARAMS_MAP.get(&ACTION_CIRCUIT_PARAMS_SIZE).unwrap(); diff --git a/taiga_halo2/src/action.rs b/taiga_halo2/src/action.rs index 77fadff6..9c7509ad 100644 --- a/taiga_halo2/src/action.rs +++ b/taiga_halo2/src/action.rs @@ -1,11 +1,11 @@ use crate::{ circuit::action_circuit::ActionCircuit, merkle_tree::{MerklePath, Node}, - note::{InputNoteProvingInfo, Note, OutputNoteProvingInfo}, + note::{InputNoteProvingInfo, Note, OutputNoteProvingInfo, RandomSeed}, nullifier::Nullifier, value_commitment::ValueCommitment, + vp_commitment::ValidityPredicateCommitment, }; -use halo2_proofs::arithmetic::Field; use pasta_curves::pallas; use rand::RngCore; @@ -28,10 +28,14 @@ pub struct ActionInstance { pub anchor: pallas::Base, /// The nullifier of input note. pub nf: Nullifier, - /// The commitment of the output note. + /// The commitment to the output note. pub cm_x: pallas::Base, - /// net value commitment + /// The commitment to net value pub cv_net: ValueCommitment, + /// The commitment to input note application(static) vp + pub input_vp_commitment: ValidityPredicateCommitment, + /// The commitment to output note application(static) vp + pub output_vp_commitment: ValidityPredicateCommitment, } /// The information to build ActionInstance and ActionCircuit. @@ -40,17 +44,24 @@ pub struct ActionInfo { input_note: Note, input_merkle_path: MerklePath, output_note: Note, - rcv: pallas::Scalar, + // rseed is to generate the randomness of the value commitment and vp commitments + rseed: RandomSeed, } impl ActionInstance { pub fn to_instance(&self) -> Vec { + let input_vp_commitment = self.input_vp_commitment.to_public_inputs(); + let output_vp_commitment = self.output_vp_commitment.to_public_inputs(); vec![ self.nf.inner(), self.anchor, self.cm_x, self.cv_net.get_x(), self.cv_net.get_y(), + input_vp_commitment[0], + input_vp_commitment[1], + output_vp_commitment[0], + output_vp_commitment[1], ] } } @@ -63,6 +74,8 @@ impl BorshSerialize for ActionInstance { writer.write_all(&self.nf.to_bytes())?; writer.write_all(&self.cm_x.to_repr())?; writer.write_all(&self.cv_net.to_bytes())?; + writer.write_all(&self.input_vp_commitment.to_bytes())?; + writer.write_all(&self.output_vp_commitment.to_bytes())?; Ok(()) } } @@ -84,12 +97,20 @@ impl BorshDeserialize for ActionInstance { let cv_net_bytes = <[u8; 32]>::deserialize_reader(reader)?; let cv_net = Option::from(ValueCommitment::from_bytes(cv_net_bytes)) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "cv_net not in field"))?; + let input_vp_commitment_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let input_vp_commitment = + ValidityPredicateCommitment::from_bytes(input_vp_commitment_bytes); + let output_vp_commitment_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let output_vp_commitment = + ValidityPredicateCommitment::from_bytes(output_vp_commitment_bytes); Ok(ActionInstance { anchor, nf, cm_x, cv_net, + input_vp_commitment, + output_vp_commitment, }) } } @@ -99,13 +120,13 @@ impl ActionInfo { input_note: Note, input_merkle_path: MerklePath, output_note: Note, - rcv: pallas::Scalar, + rseed: RandomSeed, ) -> Self { Self { input_note, input_merkle_path, output_note, - rcv, + rseed, } } @@ -114,17 +135,28 @@ impl ActionInfo { output: OutputNoteProvingInfo, mut rng: R, ) -> Self { - let rcv = pallas::Scalar::random(&mut rng); + let rseed = RandomSeed::random(&mut rng); Self { input_note: input.note, input_merkle_path: input.merkle_path, output_note: output.note, - rcv, + rseed, } } + // Get the randomness of value commitment pub fn get_rcv(&self) -> pallas::Scalar { - self.rcv + self.rseed.get_rcv() + } + + // Get the randomness of input note application vp commitment + pub fn get_input_vp_com_r(&self) -> pallas::Base { + self.rseed.get_input_vp_cm_r() + } + + // Get the randomness of output note application vp commitment + pub fn get_output_vp_com_r(&self) -> pallas::Base { + self.rseed.get_output_vp_cm_r() } pub fn build(&self) -> (ActionInstance, ActionCircuit) { @@ -140,19 +172,33 @@ impl ActionInfo { self.input_merkle_path.root(cm_node).inner() }; - let cv_net = ValueCommitment::new(&self.input_note, &self.output_note, &self.rcv); + let rcv = self.get_rcv(); + let cv_net = ValueCommitment::new(&self.input_note, &self.output_note, &rcv); + + let input_vp_cm_r = self.get_input_vp_com_r(); + let input_vp_commitment = + ValidityPredicateCommitment::commit(&self.input_note.get_app_vk(), &input_vp_cm_r); + + let output_vp_cm_r = self.get_output_vp_com_r(); + let output_vp_commitment = + ValidityPredicateCommitment::commit(&self.output_note.get_app_vk(), &output_vp_cm_r); + let action = ActionInstance { nf, cm_x, anchor, cv_net, + input_vp_commitment, + output_vp_commitment, }; let action_circuit = ActionCircuit { input_note: self.input_note, merkle_path: self.input_merkle_path.get_path().try_into().unwrap(), output_note: self.output_note, - rcv: self.rcv, + rcv, + input_vp_cm_r, + output_vp_cm_r, }; (action, action_circuit) @@ -165,15 +211,14 @@ pub mod tests { use crate::constant::TAIGA_COMMITMENT_TREE_DEPTH; use crate::merkle_tree::MerklePath; use crate::note::tests::{random_input_note, random_output_note}; - use halo2_proofs::arithmetic::Field; - use pasta_curves::pallas; + use crate::note::RandomSeed; use rand::RngCore; pub fn random_action_info(mut rng: R) -> ActionInfo { let input_note = random_input_note(&mut rng); let output_note = random_output_note(&mut rng, input_note.get_nf().unwrap()); let input_merkle_path = MerklePath::random(&mut rng, TAIGA_COMMITMENT_TREE_DEPTH); - let rcv = pallas::Scalar::random(&mut rng); - ActionInfo::new(input_note, input_merkle_path, output_note, rcv) + let rseed = RandomSeed::random(&mut rng); + ActionInfo::new(input_note, input_merkle_path, output_note, rseed) } } diff --git a/taiga_halo2/src/circuit/action_circuit.rs b/taiga_halo2/src/circuit/action_circuit.rs index 3f733306..37bf5692 100644 --- a/taiga_halo2/src/circuit/action_circuit.rs +++ b/taiga_halo2/src/circuit/action_circuit.rs @@ -1,4 +1,5 @@ -use crate::circuit::gadgets::add::AddChip; +use crate::circuit::blake2s::{vp_commitment_gadget, Blake2sChip, Blake2sConfig}; +use crate::circuit::gadgets::{add::AddChip, assign_free_advice}; use crate::circuit::hash_to_curve::HashToCurveConfig; use crate::circuit::integrity::{check_input_note, check_output_note, compute_value_commitment}; use crate::circuit::merkle_circuit::{ @@ -7,16 +8,17 @@ use crate::circuit::merkle_circuit::{ use crate::circuit::note_circuit::{NoteChip, NoteCommitmentChip, NoteConfig}; use crate::constant::{ NoteCommitmentDomain, NoteCommitmentHashDomain, TaigaFixedBases, - ACTION_ANCHOR_PUBLIC_INPUT_ROW_IDX, ACTION_NET_VALUE_CM_X_PUBLIC_INPUT_ROW_IDX, - ACTION_NET_VALUE_CM_Y_PUBLIC_INPUT_ROW_IDX, ACTION_NF_PUBLIC_INPUT_ROW_IDX, - ACTION_OUTPUT_CM_PUBLIC_INPUT_ROW_IDX, TAIGA_COMMITMENT_TREE_DEPTH, + ACTION_ANCHOR_PUBLIC_INPUT_ROW_IDX, ACTION_INPUT_VP_CM_1_ROW_IDX, ACTION_INPUT_VP_CM_2_ROW_IDX, + ACTION_NET_VALUE_CM_X_PUBLIC_INPUT_ROW_IDX, ACTION_NET_VALUE_CM_Y_PUBLIC_INPUT_ROW_IDX, + ACTION_NF_PUBLIC_INPUT_ROW_IDX, ACTION_OUTPUT_CM_PUBLIC_INPUT_ROW_IDX, + ACTION_OUTPUT_VP_CM_1_ROW_IDX, ACTION_OUTPUT_VP_CM_2_ROW_IDX, TAIGA_COMMITMENT_TREE_DEPTH, }; use crate::merkle_tree::LR; use crate::note::Note; use halo2_gadgets::{ecc::chip::EccChip, sinsemilla::chip::SinsemillaChip}; use halo2_proofs::{ - circuit::{floor_planner, Layouter}, + circuit::{floor_planner, Layouter, Value}, plonk::{Advice, Circuit, Column, ConstraintSystem, Constraints, Error, Instance, Selector}, poly::Rotation, }; @@ -30,6 +32,7 @@ pub struct ActionConfig { merkle_config: MerklePoseidonConfig, merkle_path_selector: Selector, hash_to_curve_config: HashToCurveConfig, + blake2s_config: Blake2sConfig, } /// The Action circuit. @@ -43,6 +46,10 @@ pub struct ActionCircuit { pub output_note: Note, /// random scalar for net value commitment pub rcv: pallas::Scalar, + /// The randomness for input note application vp commitment + pub input_vp_cm_r: pallas::Base, + /// The randomness for output note application vp commitment + pub output_vp_cm_r: pallas::Base, } impl Circuit for ActionCircuit { @@ -101,6 +108,8 @@ impl Circuit for ActionCircuit { let hash_to_curve_config = HashToCurveConfig::configure(meta, advices, note_config.poseidon_config.clone()); + let blake2s_config = Blake2sConfig::configure(meta, advices); + Self::Config { instances, advices, @@ -108,6 +117,7 @@ impl Circuit for ActionCircuit { merkle_config, merkle_path_selector, hash_to_curve_config, + blake2s_config, } } @@ -139,6 +149,9 @@ impl Circuit for ActionCircuit { // Construct a merkle chip let merkle_chip = MerklePoseidonChip::construct(config.merkle_config); + // Construct a blake2s chip + let blake2s_chip = Blake2sChip::construct(config.blake2s_config); + // Input note // Check the input note commitment let input_note_variables = check_input_note( @@ -162,8 +175,6 @@ impl Circuit for ActionCircuit { &self.merkle_path, )?; - // TODO: user send address VP commitment and application VP commitment - // Output note let output_note_vars = check_output_note( layouter.namespace(|| "check output note"), @@ -178,10 +189,6 @@ impl Circuit for ActionCircuit { ACTION_OUTPUT_CM_PUBLIC_INPUT_ROW_IDX, )?; - // TODO: application VP commitment - - // TODO: add note verifiable encryption - // compute and public net value commitment(input_value_commitment - output_value_commitment) let cv_net = compute_value_commitment( layouter.namespace(|| "net value commitment"), @@ -231,6 +238,52 @@ impl Circuit for ActionCircuit { }, )?; + // Input note application VP commitment + let input_vp_cm_r = assign_free_advice( + layouter.namespace(|| "witness input_vp_cm_r"), + config.advices[0], + Value::known(self.input_vp_cm_r), + )?; + let input_vp_commitment = vp_commitment_gadget( + &mut layouter, + &blake2s_chip, + input_note_variables.note_variables.app_vk.clone(), + input_vp_cm_r, + )?; + layouter.constrain_instance( + input_vp_commitment[0].cell(), + config.instances, + ACTION_INPUT_VP_CM_1_ROW_IDX, + )?; + layouter.constrain_instance( + input_vp_commitment[1].cell(), + config.instances, + ACTION_INPUT_VP_CM_2_ROW_IDX, + )?; + + // Output note application VP commitment + let output_vp_cm_r = assign_free_advice( + layouter.namespace(|| "witness output_vp_cm_r"), + config.advices[0], + Value::known(self.output_vp_cm_r), + )?; + let output_vp_commitment = vp_commitment_gadget( + &mut layouter, + &blake2s_chip, + output_note_vars.note_variables.app_vk.clone(), + output_vp_cm_r, + )?; + layouter.constrain_instance( + output_vp_commitment[0].cell(), + config.instances, + ACTION_OUTPUT_VP_CM_1_ROW_IDX, + )?; + layouter.constrain_instance( + output_vp_commitment[1].cell(), + config.instances, + ACTION_OUTPUT_VP_CM_2_ROW_IDX, + )?; + Ok(()) } } diff --git a/taiga_halo2/src/circuit/blake2s.rs b/taiga_halo2/src/circuit/blake2s.rs index 284a6271..40666e77 100644 --- a/taiga_halo2/src/circuit/blake2s.rs +++ b/taiga_halo2/src/circuit/blake2s.rs @@ -108,7 +108,7 @@ impl Blake2sByte { } pub fn from_u8( - value: u8, + value: Value, mut layouter: impl Layouter, config: &Blake2sConfig, ) -> Result { @@ -119,21 +119,16 @@ impl Blake2sByte { let mut byte = value; let mut bits = Vec::with_capacity(8); for i in 0..8 { - let bit = byte & 1; - let bit_var = region.assign_advice( - || "bit", - config.advices[i], - 0, - || Value::known(F::from(bit as u64)), - )?; + let bit = byte.map(|b| F::from((b & 1) as u64)); + let bit_var = region.assign_advice(|| "bit", config.advices[i], 0, || bit)?; bits.push(bit_var); - byte >>= 1; + byte = byte.map(|b| b >> 1); } let byte = region.assign_advice( || "byte", config.advices[0], 1, - || Value::known(F::from(value as u64)), + || value.map(|v| F::from(v as u64)), )?; Ok(Self { byte, @@ -342,8 +337,7 @@ impl Blake2sChip { personalization: &[u8], ) -> Result>, Error> { assert_eq!(personalization.len(), 8); - // TODO: add padding - assert!(inputs.len() == 2); + assert!(inputs.len() % 2 == 0); // Init let mut h = vec![ @@ -735,14 +729,13 @@ impl Blake2sChip { // the decomposition from bytes to bits let mut bits = vec![]; let mut bytes = vec![]; - field.value().map(|f| { - f.to_repr().as_ref().iter().for_each(|&b| { - let byte = Blake2sByte::from_u8(b, layouter.namespace(|| "from_u8"), &self.config) - .unwrap(); - bits.append(&mut byte.get_bits().to_vec()); - bytes.push(byte.get_byte()); - }) - }); + for i in 0..32 { + let byte_value = field.value().map(|f| f.to_repr().as_ref()[i]); + let byte = + Blake2sByte::from_u8(byte_value, layouter.namespace(|| "from_u8"), &self.config)?; + bits.append(&mut byte.get_bits().to_vec()); + bytes.push(byte.get_byte()); + } // Check the decomposition from words to bytes let mut words = vec![]; @@ -1038,14 +1031,14 @@ impl Blake2sWord { ) -> Result { let mut bytes = Vec::with_capacity(4); let mut bits = Vec::with_capacity(32); - word.value().map(|v| { - for b in v.to_repr().as_ref().iter().take(4) { - let byte = Blake2sByte::from_u8(*b, layouter.namespace(|| "from_u8"), &chip.config) - .unwrap(); - bits.append(&mut byte.get_bits().to_vec()); - bytes.push(byte.get_byte()); - } - }); + for i in 0..4 { + let byte_value = word.value().map(|v| v.to_repr().as_ref()[i]); + let byte = + Blake2sByte::from_u8(byte_value, layouter.namespace(|| "from_u8"), &chip.config)?; + bits.append(&mut byte.get_bits().to_vec()); + bytes.push(byte.get_byte()); + } + chip.word_decompose(layouter.namespace(|| "word decompose"), &bytes, &word)?; Ok(Self { word, @@ -1061,7 +1054,7 @@ fn test_blake2s_circuit() { vp_commitment::ValidityPredicateCommitment, }; use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner, Value}, + circuit::{floor_planner, Layouter, Value}, dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; @@ -1072,7 +1065,7 @@ fn test_blake2s_circuit() { impl Circuit for MyCircuit { type Config = Blake2sConfig; - type FloorPlanner = SimpleFloorPlanner; + type FloorPlanner = floor_planner::V1; fn without_witnesses(&self) -> Self { Self::default() diff --git a/taiga_halo2/src/constant.rs b/taiga_halo2/src/constant.rs index 5015908a..4ea53f6d 100644 --- a/taiga_halo2/src/constant.rs +++ b/taiga_halo2/src/constant.rs @@ -30,6 +30,9 @@ pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Taiga_ExpandSeed"; pub const PRF_EXPAND_PSI: u8 = 0; pub const PRF_EXPAND_RCM: u8 = 1; pub const PRF_EXPAND_PUBLIC_INPUT_PADDING: u8 = 2; +pub const PRF_EXPAND_VCM_R: u8 = 3; +pub const PRF_EXPAND_INPUT_VP_CM_R: u8 = 4; +pub const PRF_EXPAND_OUTPUT_VP_CM_R: u8 = 5; /// Commitment merkle tree depth pub const TAIGA_COMMITMENT_TREE_DEPTH: usize = 32; @@ -44,6 +47,10 @@ pub const ACTION_ANCHOR_PUBLIC_INPUT_ROW_IDX: usize = 1; pub const ACTION_OUTPUT_CM_PUBLIC_INPUT_ROW_IDX: usize = 2; pub const ACTION_NET_VALUE_CM_X_PUBLIC_INPUT_ROW_IDX: usize = 3; pub const ACTION_NET_VALUE_CM_Y_PUBLIC_INPUT_ROW_IDX: usize = 4; +pub const ACTION_INPUT_VP_CM_1_ROW_IDX: usize = 5; +pub const ACTION_INPUT_VP_CM_2_ROW_IDX: usize = 6; +pub const ACTION_OUTPUT_VP_CM_1_ROW_IDX: usize = 7; +pub const ACTION_OUTPUT_VP_CM_2_ROW_IDX: usize = 8; pub const POSEIDON_TO_CURVE_INPUT_LEN: usize = 3; pub const CURVE_ID: &str = "pallas"; @@ -98,8 +105,7 @@ lazy_static! { }; } -pub const CIRCUIT_PARAMS_SIZE_12: u32 = 12; -pub const ACTION_CIRCUIT_PARAMS_SIZE: u32 = 12; +pub const ACTION_CIRCUIT_PARAMS_SIZE: u32 = 15; pub const VP_CIRCUIT_PARAMS_SIZE: u32 = 12; // Setup params map @@ -107,7 +113,7 @@ lazy_static! { pub static ref SETUP_PARAMS_MAP: HashMap> = { let mut m = HashMap::new(); #[allow(clippy::single_element_loop)] - for circuit_size in [CIRCUIT_PARAMS_SIZE_12] { + for circuit_size in [ACTION_CIRCUIT_PARAMS_SIZE, VP_CIRCUIT_PARAMS_SIZE] { let params = Params::new(circuit_size); m.insert(circuit_size, params); } diff --git a/taiga_halo2/src/note.rs b/taiga_halo2/src/note.rs index 0f39c53e..280b2c08 100644 --- a/taiga_halo2/src/note.rs +++ b/taiga_halo2/src/note.rs @@ -5,8 +5,8 @@ use crate::{ }, constant::{ BASE_BITS_NUM, NOTE_COMMIT_DOMAIN, NUM_NOTE, POSEIDON_TO_CURVE_INPUT_LEN, - PRF_EXPAND_PERSONALIZATION, PRF_EXPAND_PSI, PRF_EXPAND_PUBLIC_INPUT_PADDING, - PRF_EXPAND_RCM, + PRF_EXPAND_INPUT_VP_CM_R, PRF_EXPAND_OUTPUT_VP_CM_R, PRF_EXPAND_PERSONALIZATION, + PRF_EXPAND_PSI, PRF_EXPAND_PUBLIC_INPUT_PADDING, PRF_EXPAND_RCM, PRF_EXPAND_VCM_R, }, merkle_tree::MerklePath, nullifier::{Nullifier, NullifierKeyContainer}, @@ -497,6 +497,39 @@ impl RandomSeed { }) .collect() } + + pub fn get_rcv(&self) -> pallas::Scalar { + let mut h = Blake2bParams::new() + .hash_length(64) + .personal(PRF_EXPAND_PERSONALIZATION) + .to_state(); + h.update(&[PRF_EXPAND_VCM_R]); + h.update(&self.0); + let bytes = *h.finalize().as_array(); + pallas::Scalar::from_uniform_bytes(&bytes) + } + + pub fn get_input_vp_cm_r(&self) -> pallas::Base { + let mut h = Blake2bParams::new() + .hash_length(64) + .personal(PRF_EXPAND_PERSONALIZATION) + .to_state(); + h.update(&[PRF_EXPAND_INPUT_VP_CM_R]); + h.update(&self.0); + let bytes = *h.finalize().as_array(); + pallas::Base::from_uniform_bytes(&bytes) + } + + pub fn get_output_vp_cm_r(&self) -> pallas::Base { + let mut h = Blake2bParams::new() + .hash_length(64) + .personal(PRF_EXPAND_PERSONALIZATION) + .to_state(); + h.update(&[PRF_EXPAND_OUTPUT_VP_CM_R]); + h.update(&self.0); + let bytes = *h.finalize().as_array(); + pallas::Base::from_uniform_bytes(&bytes) + } } impl InputNoteProvingInfo { diff --git a/taiga_halo2/src/vp_commitment.rs b/taiga_halo2/src/vp_commitment.rs index 7c28ff5b..d200a709 100644 --- a/taiga_halo2/src/vp_commitment.rs +++ b/taiga_halo2/src/vp_commitment.rs @@ -2,8 +2,11 @@ use crate::constant::VP_COMMITMENT_PERSONALIZATION; use blake2s_simd::Params; use byteorder::{ByteOrder, LittleEndian}; use ff::PrimeField; +#[cfg(feature = "serde")] +use serde; #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ValidityPredicateCommitment([u8; 32]); impl ValidityPredicateCommitment {