Skip to content

Commit

Permalink
Do not take in root in merkle_path_check().
Browse files Browse the repository at this point in the history
In the Orchard circuit, the root (i.e. anchor) is a public input.
We will not have direct access to its value in the circuit, but
will instead constrain the output of the Merkle hash to be equal
to the corresponding cell in the public input column.
  • Loading branch information
therealyingtong committed Jun 4, 2021
1 parent 37ca393 commit 23e5018
Showing 1 changed file with 23 additions and 63 deletions.
86 changes: 23 additions & 63 deletions src/circuit/gadget/merkle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use halo2::{
circuit::{Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Permutation, Selector},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Permutation},
poly::Rotation,
};
use pasta_curves::arithmetic::{CurveAffine, FieldExt};
Expand Down Expand Up @@ -37,7 +37,6 @@ pub trait MerkleInstructions<
layouter: impl Layouter<C::Base>,
domain: &<Self as SinsemillaInstructions<C, K, MAX_WORDS>>::HashDomains,
start_height: usize,
root: Option<C::Base>,
node: (<Self as UtilitiesInstructions<C::Base>>::Var, Option<u32>),
merkle_path: Vec<Option<C::Base>>,
) -> Result<<Self as UtilitiesInstructions<C::Base>>::Var, Error>;
Expand All @@ -57,7 +56,6 @@ pub trait MerkleInstructions<

#[derive(Clone, Debug)]
pub struct MerkleConfig<const PATH_LENGTH: usize> {
q_merkle: Selector,
a: Column<Advice>,
b: Column<Advice>,
c: Column<Advice>,
Expand Down Expand Up @@ -103,7 +101,6 @@ impl<C: CurveAffine, const PATH_LENGTH: usize, const K: usize, const MAX_WORDS:
let left = advices[3];
let right = advices[4];

let q_merkle = meta.selector();
let l_star = meta.fixed_column();

// Check that pieces have been decomposed correctly for Sinsemilla hash.
Expand Down Expand Up @@ -151,17 +148,7 @@ impl<C: CurveAffine, const PATH_LENGTH: usize, const K: usize, const MAX_WORDS:
.collect()
});

// Check that root is recovered at the end of Merkle hash.
meta.create_gate("Root check", |meta| {
let q_merkle = meta.query_selector(q_merkle, Rotation::cur());
let root = meta.query_advice(a, Rotation::cur());
let computed_root = meta.query_advice(b, Rotation::cur());

vec![q_merkle * (root - computed_root)]
});

MerkleConfig {
q_merkle,
a,
b,
c,
Expand Down Expand Up @@ -228,14 +215,13 @@ fn prepare_message_piece<F: FieldExt>(
impl<C: CurveAffine, const PATH_LENGTH: usize, const K: usize, const MAX_WORDS: usize>
MerkleInstructions<C, PATH_LENGTH, K, MAX_WORDS> for MerkleChip<C, PATH_LENGTH, K, MAX_WORDS>
{
/// Check the validity of a Merkle path from a given leaf to a claimed root.
/// Hash a Merkle path from a given leaf and output the root.
#[allow(non_snake_case)]
fn merkle_path_check(
&self,
mut layouter: impl Layouter<C::Base>,
domain: &<Self as SinsemillaInstructions<C, K, MAX_WORDS>>::HashDomains,
start_height: usize,
root: Option<C::Base>,
node: (<Self as UtilitiesInstructions<C::Base>>::Var, Option<u32>),
merkle_path: Vec<Option<C::Base>>,
) -> Result<<Self as UtilitiesInstructions<C::Base>>::Var, Error> {
Expand Down Expand Up @@ -300,31 +286,6 @@ impl<C: CurveAffine, const PATH_LENGTH: usize, const K: usize, const MAX_WORDS:
)?;
}

// Check that the final node is equal to the claimed root.
layouter.assign_region(
|| "Root check",
|mut region| {
region.assign_advice(
|| "Witness root",
config.a,
0,
|| root.ok_or(Error::SynthesisError),
)?;

// Copy final output of Sinsemilla hash
copy(
&mut region,
|| "Copy final hash output",
config.b,
0,
&node,
&config.perm,
)?;

config.q_merkle.enable(&mut region, 0)
},
)?;

Ok(node)
}

Expand Down Expand Up @@ -685,7 +646,7 @@ pub mod tests {

use crate::circuit::gadget::{
sinsemilla::chip::{SinsemillaChip, SinsemillaHashDomains},
utilities::UtilitiesInstructions,
utilities::{UtilitiesInstructions, Var},
};
use crate::constants::{
util::i2lebsp, L_ORCHARD_BASE, MERKLE_CRH_PERSONALIZATION, MERKLE_DEPTH_ORCHARD,
Expand All @@ -710,7 +671,6 @@ pub mod tests {
const K: usize,
const MAX_WORDS: usize,
> {
root: Option<C::Base>,
leaf: (Option<C::Base>, Option<u32>),
merkle_path: Vec<Option<C::Base>>,
_marker: PhantomData<C>,
Expand Down Expand Up @@ -783,8 +743,9 @@ pub mod tests {
let merkle_chip_1 = MerkleChip::<pallas::Affine, PATH_LENGTH, K, MAX_WORDS>::construct(
config.0.clone(),
);
let merkle_chip_2 =
MerkleChip::<pallas::Affine, PATH_LENGTH, K, MAX_WORDS>::construct(config.1);
let merkle_chip_2 = MerkleChip::<pallas::Affine, PATH_LENGTH, K, MAX_WORDS>::construct(
config.1.clone(),
);

// Process lo half of the Merkle path from leaf to intermediate root.
let leaf = merkle_chip_1.load_private(
Expand All @@ -793,41 +754,40 @@ pub mod tests {
self.leaf.0,
)?;
let pos_lo = self.leaf.1.map(|pos| pos & ((1 << PATH_LENGTH) - 1));
let pos_lo_bool = pos_lo.map(i2lebsp::<16>);

let path_lo: Option<Vec<pallas::Base>> = self.merkle_path[0..PATH_LENGTH]
.to_vec()
.into_iter()
.collect();

let intermediate_root = self
.leaf
.0
.zip(pos_lo_bool)
.zip(path_lo)
.map(|((leaf, pos), path)| hash_path(&merkle_crh, 0, leaf, &pos, &path));

let intermediate_root = merkle_chip_1.merkle_path_check(
layouter.namespace(|| ""),
&SinsemillaHashDomains::MerkleCrh,
0,
intermediate_root,
(leaf, pos_lo),
self.merkle_path[0..PATH_LENGTH].to_vec(),
)?;

// Process hi half of the Merkle path from intermediate root to root.
let pos_hi = self.leaf.1.map(|pos| pos >> (PATH_LENGTH));

merkle_chip_2.merkle_path_check(
let computed_final_root = merkle_chip_2.merkle_path_check(
layouter.namespace(|| ""),
&SinsemillaHashDomains::MerkleCrh,
PATH_LENGTH,
self.root,
(intermediate_root, pos_hi),
self.merkle_path[PATH_LENGTH..].to_vec(),
)?;

// The expected final root
let pos_bool = i2lebsp::<32>(self.leaf.1.unwrap());
let path: Option<Vec<pallas::Base>> = self.merkle_path.to_vec().into_iter().collect();
let final_root = hash_path(
&merkle_crh,
0,
self.leaf.0.unwrap(),
&pos_bool,
&path.unwrap(),
);

// Check the computed final root against the expected final root.
assert_eq!(computed_final_root.value().unwrap(), final_root);

Ok(())
}
}
Expand Down Expand Up @@ -888,10 +848,10 @@ pub mod tests {
.map(|_| pallas::Base::rand())
.collect();

let root = hash_path(&merkle_crh, 0, leaf, &pos_bool, &path);
// This root is provided as a public input in the Orchard circuit.
let _root = hash_path(&merkle_crh, 0, leaf, &pos_bool, &path);

let circuit = MyCircuit::<pallas::Affine, { MERKLE_DEPTH_ORCHARD / 2 }, K, C> {
root: Some(root),
leaf: (Some(leaf), Some(pos)),
merkle_path: path.into_iter().map(Some).collect(),
_marker: PhantomData,
Expand Down

0 comments on commit 23e5018

Please sign in to comment.