mirror of https://github.com/zcash/orchard.git
sinsemilla::merkle.rs: Configure MerkleChip
MerkleChip::configure() takes a SinsemillaConfig as input.
This commit is contained in:
parent
6976e2baeb
commit
569eb4baa6
|
@ -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<Fixed>,
|
||||
pub(super) constants: Column<Fixed>,
|
||||
/// 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.
|
||||
|
|
|
@ -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<pallas::Base> for MerkleChip {
|
|||
}
|
||||
}
|
||||
|
||||
impl MerkleChip {
|
||||
pub fn configure(
|
||||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
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.
|
||||
// <https://zips.z.cash/protocol/nu5.pdf#orchardmerklecrh>
|
||||
//
|
||||
// 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<pallas::Base> for MerkleChip {
|
||||
type Var = CellValue<pallas::Base>;
|
||||
}
|
||||
|
@ -133,24 +240,3 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
|
|||
SinsemillaChip::extract(point)
|
||||
}
|
||||
}
|
||||
|
||||
fn bitrange_subset(field_elem: pallas::Base, bitrange: std::ops::Range<usize>) -> pallas::Base {
|
||||
let bits = &field_elem
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_val()
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect::<Vec<_>>()[bitrange];
|
||||
let bits: Vec<bool> = bits
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(std::iter::repeat(false))
|
||||
.take(256)
|
||||
.collect();
|
||||
let bytearray: Vec<u8> = 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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue