Skip to content

Commit

Permalink
ZEC/ZSA hash optim
Browse files Browse the repository at this point in the history
  • Loading branch information
ConstanceBeguier committed Oct 5, 2023
1 parent a98481f commit 24f6ebe
Show file tree
Hide file tree
Showing 4 changed files with 2,127 additions and 1,018 deletions.
64 changes: 63 additions & 1 deletion src/circuit/gadget/mux_chip.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use halo2_gadgets::ecc::chip::EccPoint;
use halo2_gadgets::ecc::chip::{EccPoint, NonIdentityEccPoint};
use halo2_proofs::{
circuit::{AssignedCell, Chip, Layouter, Value},
plonk::{self, Advice, Column, ConstraintSystem, Constraints, Expression, Selector},
Expand Down Expand Up @@ -85,6 +85,16 @@ pub(crate) trait MuxInstructions {
left: &EccPoint,
right: &EccPoint,
) -> Result<EccPoint, plonk::Error>;

/// Constraints `MUX(choice, left, right)` and returns the selected point.
/// `left`, `right` are non identity points.
fn mux_for_non_identity_point(
&self,
layouter: impl Layouter<pallas::Base>,
choice: &AssignedCell<pallas::Base, pallas::Base>,
left: &NonIdentityEccPoint,
right: &NonIdentityEccPoint,
) -> Result<NonIdentityEccPoint, plonk::Error>;
}

impl MuxInstructions for MuxChip {
Expand Down Expand Up @@ -139,6 +149,58 @@ impl MuxInstructions for MuxChip {
y_cell.into(),
))
}

fn mux_for_non_identity_point(
&self,
mut layouter: impl Layouter<pallas::Base>,
choice: &AssignedCell<pallas::Base, pallas::Base>,
left: &NonIdentityEccPoint,
right: &NonIdentityEccPoint,
) -> Result<NonIdentityEccPoint, plonk::Error> {
let x_cell = layouter.assign_region(
|| "mux x",
|mut region| {
self.config.q_mux.enable(&mut region, 0)?;

choice.copy_advice(|| "copy choice", &mut region, self.config.choice, 0)?;
left.x()
.copy_advice(|| "copy left_x", &mut region, self.config.left, 0)?;
right
.x()
.copy_advice(|| "copy right_x", &mut region, self.config.right, 0)?;

let out_val = (Value::known(pallas::Base::one()) - choice.value())
* left.x().value()
+ choice.value() * right.x().value();

region.assign_advice(|| "out x", self.config.out, 0, || out_val)
},
)?;
let y_cell = layouter.assign_region(
|| "mux y",
|mut region| {
self.config.q_mux.enable(&mut region, 0)?;

choice.copy_advice(|| "copy choice", &mut region, self.config.choice, 0)?;
left.y()
.copy_advice(|| "copy left_y", &mut region, self.config.left, 0)?;
right
.y()
.copy_advice(|| "copy right_y", &mut region, self.config.right, 0)?;

let out_val = (Value::known(pallas::Base::one()) - choice.value())
* left.y().value()
+ choice.value() * right.y().value();

region.assign_advice(|| "out y", self.config.out, 0, || out_val)
},
)?;

Ok(NonIdentityEccPoint::from_coordinates_unchecked(
x_cell.into(),
y_cell.into(),
))
}
}

#[cfg(test)]
Expand Down
105 changes: 62 additions & 43 deletions src/circuit/note_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
use halo2_gadgets::{
ecc::{
chip::{EccChip, NonIdentityEccPoint},
Point, ScalarFixed,
NonIdentityPoint, Point, ScalarFixed,
},
sinsemilla::{
chip::{SinsemillaChip, SinsemillaConfig},
Expand Down Expand Up @@ -1847,8 +1847,8 @@ pub(in crate::circuit) mod gadgets {
//
// https://p.z.cash/ZKS:action-cm-old-integrity?partial
// https://p.z.cash/ZKS:action-cmx-new-integrity?partial
let (cm, zs) = {
let message_zec = Message::from_pieces(
let (cm, zs_common, zs_zsa_suffix) = {
let message_common_prefix = Message::from_pieces(
chip.clone(),
vec![
a.clone(),
Expand All @@ -1858,25 +1858,13 @@ pub(in crate::circuit) mod gadgets {
e.clone(),
f.clone(),
g.clone(),
h_zec.clone(),
],
);

let message_zsa = Message::from_pieces(
chip.clone(),
vec![
a.clone(),
b.clone(),
c.clone(),
d.clone(),
e.clone(),
f.clone(),
g.clone(),
h_zsa.clone(),
i.clone(),
j.clone(),
],
);
let message_suffix_zec = Message::from_pieces(chip.clone(), vec![h_zec.clone()]);

let message_suffix_zsa =
Message::from_pieces(chip.clone(), vec![h_zsa.clone(), i.clone(), j.clone()]);

let zec_domain = CommitDomain::new(
chip.clone(),
Expand All @@ -1886,22 +1874,53 @@ pub(in crate::circuit) mod gadgets {
let zsa_domain =
CommitDomain::new(chip, ecc_chip.clone(), &OrchardCommitDomains::NoteZsaCommit);

// We evaluate `hash_point_zec=hash(Q_ZEC, message_zec)` and `hash_point_zsa(Q_ZSA, message_zsa)
// and then perform a MUX to select the desired hash_point
// TODO: We can optimize the evaluation of hash_point by mutualizing a portion of the
// hash evaluation process between hash_point_zec and hash_point_zsa.
// 1. common_bits = a || b || c || d || e || f || g
// 2. suffix_zec = h_zec
// 3. suffix_zsa = h_zsa || i || j
// 4. Q = if (is_native_asset == 0) {Q_ZSA} else {Q_ZEC}
// 5. hash_prefix = hash(Q, common_bits) // this part is mutualized
// 6. hash_zec = hash(hash_prefix, suffix_zec)
// 7. hash_zsa = hash(hash_prefix, suffix_zsa)
// 8. hash_point = if (is_native_asset == 0) {hash_zsa} else {hash_zec}
let (hash_point_zec, _zs_zec) =
zec_domain.hash(layouter.namespace(|| "hash ZEC note"), message_zec)?;
let (hash_point_zsa, zs_zsa) =
zsa_domain.hash(layouter.namespace(|| "hash ZSA note"), message_zsa)?;
// Perform a MUX to select the desired initial Q point
// q_init = q_init_zec if is_native_asset is true
// q_init = q_init_zsa if is_native_asset is false
let q_init = {
let q_init_zec = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "q_init_zec"),
Value::known(zec_domain.q_init()),
)?;

let q_init_zsa = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "q_init_zsa"),
Value::known(zsa_domain.q_init()),
)?;

mux_chip.mux_for_non_identity_point(
layouter.namespace(|| "mux on hash point"),
&is_native_asset,
q_init_zsa.inner(),
q_init_zec.inner(),
)?
};

// common_hash = hash(q_init, message_common_prefix)
//
// To evaluate the different hash, we could use either zec_domain or zsa_domain
// because we use a private initial point.
let (common_hash, zs_common) = zec_domain.hash_with_private_init(
layouter.namespace(|| "hash common prefix note"),
&q_init,
message_common_prefix,
)?;

// hash_point_zec = hash(common_hash, message_suffix_zec) = hash(q_init, message_zec)
let (hash_point_zec, _zs_zec) = zec_domain.hash_with_private_init(
layouter.namespace(|| "hash suffix ZEC note"),
common_hash.inner(),
message_suffix_zec,
)?;

// hash_point_zsa = hash(common_hash, message_suffix_zsa) = hash(q_init, message_zsa)
let (hash_point_zsa, zs_zsa) = zec_domain.hash_with_private_init(
layouter.namespace(|| "hash suffix ZSA note"),
common_hash.inner(),
message_suffix_zsa,
)?;

// Perform a MUX to select the desired hash point
// hash_point = hash_zec if is_native_asset is true
Expand All @@ -1923,19 +1942,19 @@ pub(in crate::circuit) mod gadgets {
let commitment =
hash_point.add(layouter.namespace(|| "M + [r] R"), &blinding_factor)?;

(commitment, zs_zsa)
(commitment, zs_common, zs_zsa)
};

// `CommitDomain::hash` returns the running sum for each `MessagePiece`. Grab
// the outputs that we will need for canonicity checks.
let z13_a = zs[0][13].clone();
let z13_c = zs[2][13].clone();
let z1_d = zs[3][1].clone();
let z13_f = zs[5][13].clone();
let z1_g = zs[6][1].clone();
let z13_a = zs_common[0][13].clone();
let z13_c = zs_common[2][13].clone();
let z1_d = zs_common[3][1].clone();
let z13_f = zs_common[5][13].clone();
let z1_g = zs_common[6][1].clone();
let g_2 = z1_g.clone();
let z13_g = zs[6][13].clone();
let z13_i = zs[8][13].clone();
let z13_g = zs_common[6][13].clone();
let z13_i = zs_zsa_suffix[1][13].clone();

// Witness and constrain the bounds we need to ensure canonicity.
let (a_prime, z13_a_prime) = canon_bitshift_130(
Expand Down
Loading

0 comments on commit 24f6ebe

Please sign in to comment.