diff --git a/src/circuit/gadget/sinsemilla/chip.rs b/src/circuit/gadget/sinsemilla/chip.rs index 372b6234..b1452baf 100644 --- a/src/circuit/gadget/sinsemilla/chip.rs +++ b/src/circuit/gadget/sinsemilla/chip.rs @@ -61,7 +61,7 @@ pub struct SinsemillaConfig { /// Fixed column shared by the whole circuit. This is used to load the /// x-coordinate of the domain $Q$, which is then constrained to equal the /// initial $x_a$. - constants: Column, + pub(super) constants: Column, /// Permutation over all advice columns and the `constants` fixed column. pub(super) perm: Permutation, /// Configure each advice column to be able to perform lookup range checks. diff --git a/src/circuit/gadget/sinsemilla/merkle/chip.rs b/src/circuit/gadget/sinsemilla/merkle/chip.rs index af5e9e0c..b1bd1276 100644 --- a/src/circuit/gadget/sinsemilla/merkle/chip.rs +++ b/src/circuit/gadget/sinsemilla/merkle/chip.rs @@ -21,7 +21,7 @@ use crate::{ constants::MERKLE_DEPTH_ORCHARD, primitives::sinsemilla, }; -use ff::{PrimeField, PrimeFieldBits}; +use ff::PrimeFieldBits; use std::{array, convert::TryInto}; #[derive(Clone, Debug)] @@ -52,6 +52,113 @@ impl Chip for MerkleChip { } } +impl MerkleChip { + pub fn configure( + meta: &mut ConstraintSystem, + sinsemilla_config: SinsemillaConfig, + ) -> MerkleConfig { + let advices = sinsemilla_config.advices(); + let cond_swap_config = + CondSwapChip::configure(meta, advices, sinsemilla_config.perm.clone()); + let lookup_config = LookupRangeCheckConfig::configure( + meta, + advices[0], + sinsemilla_config.constants, + sinsemilla_config.generator_table.table_idx, + sinsemilla_config.perm.clone(), + ); + + // This fixed column serves two purposes: + // - Fixing the value of l* for rows in which a Merkle path layer + // is decomposed. + // - Disabling the entire decomposition gate (when set to zero) + // (i.e. replacing a Selector). + + let l_star_plus1 = meta.fixed_column(); + + // Check that pieces have been decomposed correctly for Sinsemilla hash. + // + // + // a = a_0||a_1 = l_star || (bits 0..=239 of left) + // b = b_0||b_1||b_2 + // = (bits 240..=249 of left) || (bits 250..=254 of left) || (bits 0..=4 of right) + // c = bits 5..=254 of right + // + // The message pieces `a`, `b`, `c` are constrained by Sinsemilla to be + // 250 bits, 20 bits, and 250 bits respectively. + // + meta.create_gate("Decomposition check", |meta| { + let l_star_plus1_whole = meta.query_fixed(l_star_plus1, Rotation::cur()); + + let two_pow_5 = pallas::Base::from_u64(1 << 5); + let two_pow_10 = two_pow_5.square(); + + // a_whole is constrained by Sinsemilla to be 250 bits. + let a_whole = meta.query_advice(advices[0], Rotation::cur()); + // b_whole is constrained by Sinsemilla to be 20 bits. + let b_whole = meta.query_advice(advices[1], Rotation::cur()); + // c_whole is constrained by Sinsemilla to be 250 bits. + let c_whole = meta.query_advice(advices[2], Rotation::cur()); + let left_node = meta.query_advice(advices[3], Rotation::cur()); + let right_node = meta.query_advice(advices[4], Rotation::cur()); + + // a = a_0||a_1 = l_star || (bits 0..=239 of left) + // Check that a_0 = l_star + // + // z_1 of SinsemillaHash(a) = a_1 + let z1_a = meta.query_advice(advices[0], Rotation::next()); + let a_1 = z1_a; + // a_0 = a - (a_1 * 2^10) + let a_0 = a_whole - a_1.clone() * pallas::Base::from_u64(1 << 10); + let l_star_check = + a_0 - (l_star_plus1_whole.clone() - Expression::Constant(pallas::Base::one())); + + // b = b_0||b_1||b_2 + // = (bits 240..=249 of left) || (bits 250..=254 of left) || (bits 0..=4 of right) + // + // z_1 of SinsemillaHash(b) = b_1 + 2^5 b_2 + // => b_0 = b - (z1_b * 2^10) + let z1_b = meta.query_advice(advices[1], Rotation::next()); + // b_1 has been constrained to be 5 bits outside this gate. + let b_1 = meta.query_advice(advices[2], Rotation::next()); + // b_2 has been constrained to be 5 bits outside this gate. + let b_2 = meta.query_advice(advices[3], Rotation::next()); + // Derive b_0 (constrained by SinsemillaHash to be 10 bits) + let b_0 = b_whole - (z1_b * two_pow_10); + + // Check that left = a_1 (240 bits) || b_0 (10 bits) || b_1 (5 bits) + let left_check = { + let reconstructed = { + let two_pow_240 = pallas::Base::from_u128(1 << 120).square(); + let b0_shifted = b_0 * two_pow_240; + let b1_shifted = b_1 * two_pow_240 * two_pow_10; + a_1 + b0_shifted + b1_shifted + }; + reconstructed - left_node + }; + + // Check that right = b_2 (5 bits) || c (250 bits) + let right_check = b_2 + c_whole * two_pow_5 - right_node; + + array::IntoIter::new([l_star_check, left_check, right_check]) + .map(move |poly| l_star_plus1_whole.clone() * poly) + }); + + MerkleConfig { + advices, + l_star_plus1, + perm: sinsemilla_config.perm.clone(), + cond_swap_config, + lookup_config, + sinsemilla_config, + } + } + + pub fn construct(config: MerkleConfig) -> Self { + MerkleChip { config } + } +} + impl UtilitiesInstructions for MerkleChip { type Var = CellValue; } @@ -133,24 +240,3 @@ impl SinsemillaInstructions) -> pallas::Base { - let bits = &field_elem - .to_le_bits() - .iter() - .by_val() - .take(pallas::Base::NUM_BITS as usize) - .collect::>()[bitrange]; - let bits: Vec = bits - .iter() - .cloned() - .chain(std::iter::repeat(false)) - .take(256) - .collect(); - let bytearray: Vec = bits - .chunks_exact(8) - .map(|byte| byte.iter().rev().fold(0u8, |acc, bit| acc * 2 + *bit as u8)) - .collect(); - - pallas::Base::from_bytes(&bytearray.try_into().unwrap()).unwrap() -}