2022-01-28 07:53:03 -08:00
|
|
|
use halo2_proofs::{
|
2021-12-01 18:51:46 -08:00
|
|
|
circuit::{AssignedCell, Layouter},
|
2022-04-28 10:29:49 -07:00
|
|
|
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector},
|
2021-06-06 05:11:03 -07:00
|
|
|
poly::Rotation,
|
|
|
|
};
|
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
|
|
|
|
2022-01-28 07:53:03 -08:00
|
|
|
use crate::constants::{OrchardCommitDomains, OrchardFixedBases, OrchardHashDomains, T_P};
|
|
|
|
use halo2_gadgets::{
|
|
|
|
ecc::{chip::EccChip, X},
|
|
|
|
sinsemilla::{
|
|
|
|
chip::{SinsemillaChip, SinsemillaConfig},
|
|
|
|
CommitDomain, Message, MessagePiece,
|
2021-06-06 05:11:03 -07:00
|
|
|
},
|
2022-01-28 07:53:03 -08:00
|
|
|
utilities::{bitrange_subset, bool_check},
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct CommitIvkConfig {
|
2021-07-24 11:41:54 -07:00
|
|
|
q_commit_ivk: Selector,
|
2021-06-06 05:11:03 -07:00
|
|
|
advices: [Column<Advice>; 10],
|
2021-08-18 23:59:39 -07:00
|
|
|
sinsemilla_config:
|
|
|
|
SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
|
2021-06-06 05:11:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CommitIvkConfig {
|
|
|
|
pub(in crate::circuit) fn configure(
|
|
|
|
meta: &mut ConstraintSystem<pallas::Base>,
|
|
|
|
advices: [Column<Advice>; 10],
|
2021-08-18 23:59:39 -07:00
|
|
|
sinsemilla_config: SinsemillaConfig<
|
|
|
|
OrchardHashDomains,
|
|
|
|
OrchardCommitDomains,
|
|
|
|
OrchardFixedBases,
|
|
|
|
>,
|
2021-06-06 05:11:03 -07:00
|
|
|
) -> Self {
|
2021-07-24 11:41:54 -07:00
|
|
|
let q_commit_ivk = meta.selector();
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
let config = Self {
|
2021-07-24 11:41:54 -07:00
|
|
|
q_commit_ivk,
|
2021-06-06 05:11:03 -07:00
|
|
|
advices,
|
|
|
|
sinsemilla_config,
|
|
|
|
};
|
|
|
|
|
|
|
|
// <https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
|
|
|
|
// We need to hash `ak || nk` where each of `ak`, `nk` is a field element (255 bits).
|
|
|
|
//
|
|
|
|
// a = bits 0..=249 of `ak`
|
|
|
|
// b = b_0||b_1||b_2`
|
|
|
|
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
|
|
|
|
// c = bits 5..=244 of `nk`
|
|
|
|
// d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
|
|
|
|
//
|
|
|
|
// `a`, `b`, `c`, `d` have been constrained by the Sinsemilla hash to be:
|
|
|
|
// - a: 250 bits,
|
|
|
|
// - b: 10 bits,
|
|
|
|
// - c: 240 bits,
|
|
|
|
// - d: 10 bits
|
|
|
|
//
|
2021-07-24 11:41:54 -07:00
|
|
|
/*
|
|
|
|
The pieces are laid out in this configuration:
|
|
|
|
|
|
|
|
| A_0 | A_1 | A_2 | A_3 | A_4 | A_5 | A_6 | A_7 | A_8 | q_commit_ivk |
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
2021-07-29 06:55:55 -07:00
|
|
|
| ak | a | b | b_0 | b_1 | b_2 | z13_a | a_prime | z13_a_prime | 1 |
|
|
|
|
| nk | c | d | d_0 | d_1 | | z13_c | b2_c_prime| z14_b2_c_prime | 0 |
|
2021-07-24 11:41:54 -07:00
|
|
|
|
|
|
|
*/
|
2021-06-06 05:11:03 -07:00
|
|
|
meta.create_gate("CommitIvk canonicity check", |meta| {
|
2021-07-24 11:41:54 -07:00
|
|
|
let q_commit_ivk = meta.query_selector(config.q_commit_ivk);
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// Useful constants
|
2021-12-07 09:47:03 -08:00
|
|
|
let two_pow_4 = pallas::Base::from(1 << 4);
|
|
|
|
let two_pow_5 = pallas::Base::from(1 << 5);
|
2021-06-06 05:11:03 -07:00
|
|
|
let two_pow_9 = two_pow_4 * two_pow_5;
|
|
|
|
let two_pow_250 = pallas::Base::from_u128(1 << 125).square();
|
2021-07-24 11:41:54 -07:00
|
|
|
let two_pow_254 = two_pow_250 * two_pow_4;
|
|
|
|
|
2021-07-29 06:55:55 -07:00
|
|
|
let ak = meta.query_advice(config.advices[0], Rotation::cur());
|
|
|
|
let nk = meta.query_advice(config.advices[0], Rotation::next());
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// `a` is constrained by the Sinsemilla hash to be 250 bits.
|
2021-07-29 06:55:55 -07:00
|
|
|
let a = meta.query_advice(config.advices[1], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
// `b` is constrained by the Sinsemilla hash to be 10 bits.
|
2021-07-29 06:55:55 -07:00
|
|
|
let b_whole = meta.query_advice(config.advices[2], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
// `c` is constrained by the Sinsemilla hash to be 240 bits.
|
2021-07-29 06:55:55 -07:00
|
|
|
let c = meta.query_advice(config.advices[1], Rotation::next());
|
2021-06-06 05:11:03 -07:00
|
|
|
// `d` is constrained by the Sinsemilla hash to be 10 bits.
|
2021-07-29 06:55:55 -07:00
|
|
|
let d_whole = meta.query_advice(config.advices[2], Rotation::next());
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// b = b_0||b_1||b_2`
|
|
|
|
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
|
|
|
|
//
|
|
|
|
// b_0 has been constrained outside this gate to be a four-bit value.
|
2021-07-29 06:55:55 -07:00
|
|
|
let b_0 = meta.query_advice(config.advices[3], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
// This gate constrains b_1 to be a one-bit value.
|
2021-07-29 06:55:55 -07:00
|
|
|
let b_1 = meta.query_advice(config.advices[4], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
// b_2 has been constrained outside this gate to be a five-bit value.
|
2021-07-29 06:55:55 -07:00
|
|
|
let b_2 = meta.query_advice(config.advices[5], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
// Check that b_whole is consistent with the witnessed subpieces.
|
|
|
|
let b_decomposition_check =
|
|
|
|
b_whole - (b_0.clone() + b_1.clone() * two_pow_4 + b_2.clone() * two_pow_5);
|
|
|
|
|
|
|
|
// d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
|
|
|
|
//
|
|
|
|
// d_0 has been constrained outside this gate to be a nine-bit value.
|
2021-07-29 06:55:55 -07:00
|
|
|
let d_0 = meta.query_advice(config.advices[3], Rotation::next());
|
2021-06-06 05:11:03 -07:00
|
|
|
// This gate constrains d_1 to be a one-bit value.
|
2021-07-29 06:55:55 -07:00
|
|
|
let d_1 = meta.query_advice(config.advices[4], Rotation::next());
|
2021-06-06 05:11:03 -07:00
|
|
|
// Check that d_whole is consistent with the witnessed subpieces.
|
|
|
|
let d_decomposition_check = d_whole - (d_0.clone() + d_1.clone() * two_pow_9);
|
|
|
|
|
|
|
|
// Check `b_1` is a single-bit value
|
|
|
|
let b1_bool_check = bool_check(b_1.clone());
|
|
|
|
|
|
|
|
// Check `d_1` is a single-bit value
|
|
|
|
let d1_bool_check = bool_check(d_1.clone());
|
|
|
|
|
|
|
|
// Check that ak = a (250 bits) || b_0 (4 bits) || b_1 (1 bit)
|
|
|
|
let ak_decomposition_check =
|
|
|
|
a.clone() + b_0.clone() * two_pow_250 + b_1.clone() * two_pow_254 - ak;
|
|
|
|
|
|
|
|
// Check that nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
|
|
|
|
let nk_decomposition_check = {
|
2021-12-07 09:47:03 -08:00
|
|
|
let two_pow_245 = pallas::Base::from(1 << 49).pow(&[5, 0, 0, 0]);
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
b_2.clone()
|
|
|
|
+ c.clone() * two_pow_5
|
|
|
|
+ d_0.clone() * two_pow_245
|
|
|
|
+ d_1.clone() * two_pow_254
|
|
|
|
- nk
|
|
|
|
};
|
|
|
|
|
|
|
|
// ak = a (250 bits) || b_0 (4 bits) || b_1 (1 bit)
|
|
|
|
// The `ak` canonicity checks are enforced if and only if `b_1` = 1.
|
|
|
|
let ak_canonicity_checks = {
|
|
|
|
// b_1 = 1 => b_0 = 0
|
|
|
|
let b0_canon_check = b_1.clone() * b_0;
|
|
|
|
|
|
|
|
// z13_a is the 13th running sum output by the 10-bit Sinsemilla decomposition of `a`.
|
|
|
|
// b_1 = 1 => z13_a = 0
|
|
|
|
let z13_a_check = {
|
2021-07-29 06:55:55 -07:00
|
|
|
let z13_a = meta.query_advice(config.advices[6], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
b_1.clone() * z13_a
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check that a_prime = a + 2^130 - t_P.
|
|
|
|
// This is checked regardless of the value of b_1.
|
|
|
|
let a_prime_check = {
|
2021-07-29 06:55:55 -07:00
|
|
|
let a_prime = meta.query_advice(config.advices[7], Rotation::cur());
|
2021-06-06 05:11:03 -07:00
|
|
|
let two_pow_130 =
|
|
|
|
Expression::Constant(pallas::Base::from_u128(1 << 65).square());
|
|
|
|
let t_p = Expression::Constant(pallas::Base::from_u128(T_P));
|
|
|
|
a + two_pow_130 - t_p - a_prime
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check that the running sum output by the 130-bit little-endian decomposition of
|
|
|
|
// `a_prime` is zero.
|
2021-07-24 11:41:54 -07:00
|
|
|
let z13_a_prime = {
|
2021-07-29 06:55:55 -07:00
|
|
|
let z13_a_prime = meta.query_advice(config.advices[8], Rotation::cur());
|
2021-07-24 11:41:54 -07:00
|
|
|
b_1 * z13_a_prime
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
std::iter::empty()
|
2021-07-28 18:10:32 -07:00
|
|
|
.chain(Some(("b0_canon_check", b0_canon_check)))
|
|
|
|
.chain(Some(("z13_a_check", z13_a_check)))
|
|
|
|
.chain(Some(("a_prime_check", a_prime_check)))
|
|
|
|
.chain(Some(("z13_a_prime", z13_a_prime)))
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
|
|
|
|
// The `nk` canonicity checks are enforced if and only if `d_1` = 1.
|
|
|
|
let nk_canonicity_checks = {
|
|
|
|
// d_1 = 1 => d_0 = 0
|
|
|
|
let c0_canon_check = d_1.clone() * d_0;
|
|
|
|
|
2021-07-24 10:14:29 -07:00
|
|
|
// d_1 = 1 => z13_c = 0, where z13_c is the 13th running sum
|
2021-06-06 05:11:03 -07:00
|
|
|
// output by the 10-bit Sinsemilla decomposition of `c`.
|
2021-07-24 10:14:29 -07:00
|
|
|
let z13_c_check = {
|
2021-07-29 06:55:55 -07:00
|
|
|
let z13_c = meta.query_advice(config.advices[6], Rotation::next());
|
2021-07-24 10:14:29 -07:00
|
|
|
d_1.clone() * z13_c
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// Check that b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P.
|
|
|
|
// This is checked regardless of the value of d_1.
|
|
|
|
let b2_c_prime_check = {
|
2021-12-07 09:47:03 -08:00
|
|
|
let two_pow_5 = pallas::Base::from(1 << 5);
|
2021-06-06 05:11:03 -07:00
|
|
|
let two_pow_140 =
|
|
|
|
Expression::Constant(pallas::Base::from_u128(1 << 70).square());
|
|
|
|
let t_p = Expression::Constant(pallas::Base::from_u128(T_P));
|
2021-07-29 06:55:55 -07:00
|
|
|
let b2_c_prime = meta.query_advice(config.advices[7], Rotation::next());
|
2021-06-06 05:11:03 -07:00
|
|
|
b_2 + c * two_pow_5 + two_pow_140 - t_p - b2_c_prime
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check that the running sum output by the 140-bit little-
|
|
|
|
// endian decomposition of b2_c_prime is zero.
|
2021-07-24 11:41:54 -07:00
|
|
|
let z14_b2_c_prime = {
|
2021-07-29 06:55:55 -07:00
|
|
|
let z14_b2_c_prime = meta.query_advice(config.advices[8], Rotation::next());
|
2021-07-24 11:41:54 -07:00
|
|
|
d_1 * z14_b2_c_prime
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
std::iter::empty()
|
2021-07-28 18:10:32 -07:00
|
|
|
.chain(Some(("c0_canon_check", c0_canon_check)))
|
|
|
|
.chain(Some(("z13_c_check", z13_c_check)))
|
|
|
|
.chain(Some(("b2_c_prime_check", b2_c_prime_check)))
|
|
|
|
.chain(Some(("z14_b2_c_prime", z14_b2_c_prime)))
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
2022-04-28 10:29:49 -07:00
|
|
|
Constraints::with_selector(
|
|
|
|
q_commit_ivk,
|
|
|
|
std::iter::empty()
|
|
|
|
.chain(Some(("b1_bool_check", b1_bool_check)))
|
|
|
|
.chain(Some(("d1_bool_check", d1_bool_check)))
|
|
|
|
.chain(Some(("b_decomposition_check", b_decomposition_check)))
|
|
|
|
.chain(Some(("d_decomposition_check", d_decomposition_check)))
|
|
|
|
.chain(Some(("ak_decomposition_check", ak_decomposition_check)))
|
|
|
|
.chain(Some(("nk_decomposition_check", nk_decomposition_check)))
|
|
|
|
.chain(ak_canonicity_checks)
|
|
|
|
.chain(nk_canonicity_checks),
|
|
|
|
)
|
2021-06-06 05:11:03 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
config
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
pub(in crate::circuit) fn assign_region(
|
|
|
|
&self,
|
2021-08-18 23:59:39 -07:00
|
|
|
sinsemilla_chip: SinsemillaChip<
|
|
|
|
OrchardHashDomains,
|
|
|
|
OrchardCommitDomains,
|
|
|
|
OrchardFixedBases,
|
|
|
|
>,
|
|
|
|
ecc_chip: EccChip<OrchardFixedBases>,
|
2021-06-06 05:11:03 -07:00
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
2021-12-01 18:51:46 -08:00
|
|
|
ak: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
nk: AssignedCell<pallas::Base, pallas::Base>,
|
2021-06-06 05:11:03 -07:00
|
|
|
rivk: Option<pallas::Scalar>,
|
2021-08-18 23:59:39 -07:00
|
|
|
) -> Result<X<pallas::Affine, EccChip<OrchardFixedBases>>, Error> {
|
2021-06-06 05:11:03 -07:00
|
|
|
// <https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
|
|
|
|
// We need to hash `ak || nk` where each of `ak`, `nk` is a field element (255 bits).
|
|
|
|
//
|
|
|
|
// a = bits 0..=249 of `ak`
|
|
|
|
// b = b_0||b_1||b_2`
|
|
|
|
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
|
|
|
|
// c = bits 5..=244 of `nk`
|
|
|
|
// d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
|
|
|
|
|
|
|
|
// `a` = bits 0..=249 of `ak`
|
|
|
|
let a = {
|
|
|
|
let a = ak.value().map(|value| bitrange_subset(value, 0..250));
|
|
|
|
MessagePiece::from_field_elem(
|
|
|
|
sinsemilla_chip.clone(),
|
|
|
|
layouter.namespace(|| "a"),
|
|
|
|
a,
|
|
|
|
25,
|
|
|
|
)?
|
|
|
|
};
|
|
|
|
|
|
|
|
// `b = b_0||b_1||b_2`
|
|
|
|
// = (bits 250..=253 of `ak`) || (bit 254 of `ak`) || (bits 0..=4 of `nk`)
|
|
|
|
let (b_0, b_1, b_2, b) = {
|
|
|
|
let b_0 = ak.value().map(|value| bitrange_subset(value, 250..254));
|
|
|
|
let b_1 = ak.value().map(|value| bitrange_subset(value, 254..255));
|
|
|
|
let b_2 = nk.value().map(|value| bitrange_subset(value, 0..5));
|
|
|
|
|
|
|
|
let b = b_0.zip(b_1).zip(b_2).map(|((b_0, b_1), b_2)| {
|
2021-12-07 09:47:03 -08:00
|
|
|
let b1_shifted = b_1 * pallas::Base::from(1 << 4);
|
|
|
|
let b2_shifted = b_2 * pallas::Base::from(1 << 5);
|
2021-06-06 05:11:03 -07:00
|
|
|
b_0 + b1_shifted + b2_shifted
|
|
|
|
});
|
|
|
|
|
|
|
|
// Constrain b_0 to be 4 bits.
|
2021-12-20 21:04:49 -08:00
|
|
|
let b_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "b_0 is 4 bits"),
|
|
|
|
b_0,
|
|
|
|
4,
|
|
|
|
)?;
|
|
|
|
// Constrain b_2 to be 5 bits.
|
2021-12-20 21:04:49 -08:00
|
|
|
let b_2 = self.sinsemilla_config.lookup_config().witness_short_check(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "b_2 is 5 bits"),
|
|
|
|
b_2,
|
|
|
|
5,
|
|
|
|
)?;
|
|
|
|
// b_1 will be boolean-constrained in the custom gate.
|
|
|
|
|
|
|
|
let b = MessagePiece::from_field_elem(
|
|
|
|
sinsemilla_chip.clone(),
|
|
|
|
layouter.namespace(|| "b = b_0 || b_1 || b_2"),
|
|
|
|
b,
|
|
|
|
1,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
(b_0, b_1, b_2, b)
|
|
|
|
};
|
|
|
|
|
|
|
|
// c = bits 5..=244 of `nk`
|
|
|
|
let c = {
|
|
|
|
let c = nk.value().map(|value| bitrange_subset(value, 5..245));
|
|
|
|
MessagePiece::from_field_elem(
|
|
|
|
sinsemilla_chip.clone(),
|
|
|
|
layouter.namespace(|| "c"),
|
|
|
|
c,
|
|
|
|
24,
|
|
|
|
)?
|
|
|
|
};
|
|
|
|
|
|
|
|
// `d = d_0||d_1` = (bits 245..=253 of `nk`) || (bit 254 of `nk`)
|
|
|
|
let (d_0, d_1, d) = {
|
|
|
|
let d_0 = nk.value().map(|value| bitrange_subset(value, 245..254));
|
|
|
|
let d_1 = nk.value().map(|value| bitrange_subset(value, 254..255));
|
|
|
|
|
|
|
|
let d = d_0
|
|
|
|
.zip(d_1)
|
2021-12-07 09:47:03 -08:00
|
|
|
.map(|(d_0, d_1)| d_0 + d_1 * pallas::Base::from(1 << 9));
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// Constrain d_0 to be 9 bits.
|
2021-12-20 21:04:49 -08:00
|
|
|
let d_0 = self.sinsemilla_config.lookup_config().witness_short_check(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "d_0 is 9 bits"),
|
|
|
|
d_0,
|
|
|
|
9,
|
|
|
|
)?;
|
|
|
|
// d_1 will be boolean-constrained in the custom gate.
|
|
|
|
|
|
|
|
let d = MessagePiece::from_field_elem(
|
|
|
|
sinsemilla_chip.clone(),
|
2021-07-24 11:41:54 -07:00
|
|
|
layouter.namespace(|| "d = d_0 || d_1"),
|
2021-06-06 05:11:03 -07:00
|
|
|
d,
|
|
|
|
1,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
(d_0, d_1, d)
|
|
|
|
};
|
|
|
|
|
|
|
|
let (ivk, zs) = {
|
|
|
|
let message = Message::from_pieces(
|
|
|
|
sinsemilla_chip.clone(),
|
|
|
|
vec![a.clone(), b.clone(), c.clone(), d.clone()],
|
|
|
|
);
|
2021-08-18 23:59:39 -07:00
|
|
|
let domain =
|
|
|
|
CommitDomain::new(sinsemilla_chip, ecc_chip, &OrchardCommitDomains::CommitIvk);
|
2021-06-06 05:11:03 -07:00
|
|
|
domain.short_commit(layouter.namespace(|| "Hash ak||nk"), message, rivk)?
|
|
|
|
};
|
|
|
|
|
2021-12-01 04:51:33 -08:00
|
|
|
let z13_a = zs[0][13].clone();
|
|
|
|
let z13_c = zs[2][13].clone();
|
2021-06-06 05:11:03 -07:00
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
let (a_prime, z13_a_prime) = self.ak_canonicity(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "ak canonicity"),
|
|
|
|
a.inner().cell_value(),
|
|
|
|
)?;
|
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
let (b2_c_prime, z14_b2_c_prime) = self.nk_canonicity(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "nk canonicity"),
|
2021-12-01 04:51:33 -08:00
|
|
|
b_2.clone(),
|
2021-06-06 05:11:03 -07:00
|
|
|
c.inner().cell_value(),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let gate_cells = GateCells {
|
|
|
|
a: a.inner().cell_value(),
|
|
|
|
b: b.inner().cell_value(),
|
|
|
|
c: c.inner().cell_value(),
|
|
|
|
d: d.inner().cell_value(),
|
|
|
|
ak,
|
|
|
|
nk,
|
|
|
|
b_0,
|
|
|
|
b_1,
|
|
|
|
b_2,
|
|
|
|
d_0,
|
|
|
|
d_1,
|
|
|
|
z13_a,
|
|
|
|
a_prime,
|
2021-07-24 11:41:54 -07:00
|
|
|
z13_a_prime,
|
2021-07-24 10:14:29 -07:00
|
|
|
z13_c,
|
2021-06-06 05:11:03 -07:00
|
|
|
b2_c_prime,
|
2021-07-24 11:41:54 -07:00
|
|
|
z14_b2_c_prime,
|
2021-06-06 05:11:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
self.assign_gate(
|
|
|
|
layouter.namespace(|| "Assign cells used in canonicity gate"),
|
|
|
|
gate_cells,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(ivk)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
// Check canonicity of `ak` encoding
|
|
|
|
fn ak_canonicity(
|
|
|
|
&self,
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
2021-12-01 18:51:46 -08:00
|
|
|
a: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
) -> Result<
|
|
|
|
(
|
|
|
|
AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
),
|
|
|
|
Error,
|
|
|
|
> {
|
2021-06-06 05:11:03 -07:00
|
|
|
// `ak` = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)`
|
|
|
|
// - b_1 = 1 => b_0 = 0
|
|
|
|
// - b_1 = 1 => a < t_P
|
|
|
|
// - (0 ≤ a < 2^130) => z13_a of SinsemillaHash(a) == 0
|
|
|
|
// - 0 ≤ a + 2^130 - t_P < 2^130 (thirteen 10-bit lookups)
|
|
|
|
|
|
|
|
// Decompose the low 130 bits of a_prime = a + 2^130 - t_P, and output
|
|
|
|
// the running sum at the end of it. If a_prime < 2^130, the running sum
|
|
|
|
// will be 0.
|
|
|
|
let a_prime = a.value().map(|a| {
|
|
|
|
let two_pow_130 = pallas::Base::from_u128(1u128 << 65).square();
|
|
|
|
let t_p = pallas::Base::from_u128(T_P);
|
|
|
|
a + two_pow_130 - t_p
|
|
|
|
});
|
2021-12-20 21:04:49 -08:00
|
|
|
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "Decompose low 130 bits of (a + 2^130 - t_P)"),
|
|
|
|
a_prime,
|
|
|
|
13,
|
|
|
|
false,
|
|
|
|
)?;
|
2021-12-01 04:51:33 -08:00
|
|
|
let a_prime = zs[0].clone();
|
2021-06-06 05:11:03 -07:00
|
|
|
assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z13_a]
|
|
|
|
|
2021-12-01 04:51:33 -08:00
|
|
|
Ok((a_prime, zs[13].clone()))
|
2021-06-06 05:11:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
// Check canonicity of `nk` encoding
|
|
|
|
fn nk_canonicity(
|
|
|
|
&self,
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
2021-12-01 18:51:46 -08:00
|
|
|
b_2: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
c: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
) -> Result<
|
|
|
|
(
|
|
|
|
AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
),
|
|
|
|
Error,
|
|
|
|
> {
|
2021-06-06 05:11:03 -07:00
|
|
|
// `nk` = `b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
|
|
|
|
// - d_1 = 1 => d_0 = 0
|
|
|
|
// - d_1 = 1 => b_2 + c * 2^5 < t_P
|
|
|
|
// - 0 ≤ b_2 + c * 2^5 < 2^140
|
|
|
|
// - b_2 was constrained to be 5 bits.
|
|
|
|
// - z_13 of SinsemillaHash(c) constrains bits 5..=134 to 130 bits
|
|
|
|
// - so b_2 + c * 2^5 is constrained to be 135 bits < 2^140.
|
|
|
|
// - 0 ≤ b_2 + c * 2^5 + 2^140 - t_P < 2^140 (14 ten-bit lookups)
|
|
|
|
|
|
|
|
// Decompose the low 140 bits of b2_c_prime = b_2 + c * 2^5 + 2^140 - t_P, and output
|
|
|
|
// the running sum at the end of it. If b2_c_prime < 2^140, the running sum will be 0.
|
|
|
|
let b2_c_prime = b_2.value().zip(c.value()).map(|(b_2, c)| {
|
2021-12-07 09:47:03 -08:00
|
|
|
let two_pow_5 = pallas::Base::from(1 << 5);
|
2021-06-06 05:11:03 -07:00
|
|
|
let two_pow_140 = pallas::Base::from_u128(1u128 << 70).square();
|
|
|
|
let t_p = pallas::Base::from_u128(T_P);
|
|
|
|
b_2 + c * two_pow_5 + two_pow_140 - t_p
|
|
|
|
});
|
2021-12-20 21:04:49 -08:00
|
|
|
let zs = self.sinsemilla_config.lookup_config().witness_check(
|
2021-06-06 05:11:03 -07:00
|
|
|
layouter.namespace(|| "Decompose low 140 bits of (b_2 + c * 2^5 + 2^140 - t_P)"),
|
|
|
|
b2_c_prime,
|
|
|
|
14,
|
|
|
|
false,
|
|
|
|
)?;
|
2021-12-01 04:51:33 -08:00
|
|
|
let b2_c_prime = zs[0].clone();
|
2021-06-06 05:11:03 -07:00
|
|
|
assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z14]
|
|
|
|
|
2021-12-01 04:51:33 -08:00
|
|
|
Ok((b2_c_prime, zs[14].clone()))
|
2021-06-06 05:11:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Assign cells for the canonicity gate.
|
2021-07-24 11:41:54 -07:00
|
|
|
/*
|
|
|
|
The pieces are laid out in this configuration:
|
|
|
|
|
|
|
|
| A_0 | A_1 | A_2 | A_3 | A_4 | A_5 | A_6 | A_7 | A_8 | q_commit_ivk |
|
|
|
|
-----------------------------------------------------------------------------------------------------
|
2021-07-29 06:55:55 -07:00
|
|
|
| ak | a | b | b_0 | b_1 | b_2 | z13_a | a_prime | z13_a_prime | 1 |
|
|
|
|
| nk | c | d | d_0 | d_1 | | z13_c | b2_c_prime| z14_b2_c_prime | 0 |
|
2021-07-24 11:41:54 -07:00
|
|
|
|
|
|
|
*/
|
2021-06-06 05:11:03 -07:00
|
|
|
fn assign_gate(
|
|
|
|
&self,
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
gate_cells: GateCells,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "Assign cells used in canonicity gate",
|
|
|
|
|mut region| {
|
2021-07-29 06:55:55 -07:00
|
|
|
// Enable selector on offset 0
|
|
|
|
self.q_commit_ivk.enable(&mut region, 0)?;
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// Offset 0
|
|
|
|
{
|
|
|
|
let offset = 0;
|
|
|
|
// Copy in `ak`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.ak
|
|
|
|
.copy_advice(|| "ak", &mut region, self.advices[0], offset)?;
|
2021-06-06 05:11:03 -07:00
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Copy in `a`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.a
|
|
|
|
.copy_advice(|| "a", &mut region, self.advices[1], offset)?;
|
2021-07-24 11:41:54 -07:00
|
|
|
|
|
|
|
// Copy in `b`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.b
|
|
|
|
.copy_advice(|| "b", &mut region, self.advices[2], offset)?;
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// Copy in `b_0`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.b_0
|
|
|
|
.copy_advice(|| "b_0", &mut region, self.advices[3], offset)?;
|
2021-06-06 05:11:03 -07:00
|
|
|
|
|
|
|
// Witness `b_1`
|
|
|
|
region.assign_advice(
|
|
|
|
|| "Witness b_1",
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[4],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| gate_cells.b_1.ok_or(Error::Synthesis),
|
2021-06-06 05:11:03 -07:00
|
|
|
)?;
|
|
|
|
|
|
|
|
// Copy in `b_2`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.b_2
|
|
|
|
.copy_advice(|| "b_2", &mut region, self.advices[5], offset)?;
|
2021-06-06 05:11:03 -07:00
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Copy in z13_a
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells.z13_a.copy_advice(
|
2021-07-24 11:41:54 -07:00
|
|
|
|| "z13_a",
|
2021-12-01 18:10:00 -08:00
|
|
|
&mut region,
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[6],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
|
|
|
)?;
|
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Copy in a_prime
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells.a_prime.copy_advice(
|
2021-07-24 11:41:54 -07:00
|
|
|
|| "a_prime",
|
2021-12-01 18:10:00 -08:00
|
|
|
&mut region,
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[7],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
|
|
|
)?;
|
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Copy in z13_a_prime
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells.z13_a_prime.copy_advice(
|
2021-07-24 11:41:54 -07:00
|
|
|
|| "z13_a_prime",
|
2021-12-01 18:10:00 -08:00
|
|
|
&mut region,
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[8],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
|
|
|
)?;
|
2021-07-24 11:41:54 -07:00
|
|
|
}
|
2021-06-06 05:11:03 -07:00
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Offset 1
|
|
|
|
{
|
|
|
|
let offset = 1;
|
|
|
|
|
|
|
|
// Copy in `nk`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.nk
|
|
|
|
.copy_advice(|| "nk", &mut region, self.advices[0], offset)?;
|
2021-06-06 05:11:03 -07:00
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Copy in `c`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.c
|
|
|
|
.copy_advice(|| "c", &mut region, self.advices[1], offset)?;
|
2021-07-24 11:41:54 -07:00
|
|
|
|
|
|
|
// Copy in `d`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.d
|
|
|
|
.copy_advice(|| "d", &mut region, self.advices[2], offset)?;
|
2021-07-24 11:41:54 -07:00
|
|
|
|
|
|
|
// Copy in `d_0`
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells
|
|
|
|
.d_0
|
|
|
|
.copy_advice(|| "d_0", &mut region, self.advices[3], offset)?;
|
2021-07-24 11:41:54 -07:00
|
|
|
|
|
|
|
// Witness `d_1`
|
|
|
|
region.assign_advice(
|
|
|
|
|| "Witness d_1",
|
|
|
|
self.advices[4],
|
|
|
|
offset,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| gate_cells.d_1.ok_or(Error::Synthesis),
|
2021-06-06 05:11:03 -07:00
|
|
|
)?;
|
|
|
|
|
2021-07-24 10:14:29 -07:00
|
|
|
// Copy in z13_c
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells.z13_c.copy_advice(
|
2021-07-24 10:14:29 -07:00
|
|
|
|| "z13_c",
|
2021-12-01 18:10:00 -08:00
|
|
|
&mut region,
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[6],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Copy in b2_c_prime
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells.b2_c_prime.copy_advice(
|
2021-06-06 05:11:03 -07:00
|
|
|
|| "b2_c_prime",
|
2021-12-01 18:10:00 -08:00
|
|
|
&mut region,
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[7],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
|
|
|
)?;
|
|
|
|
|
2021-07-24 11:41:54 -07:00
|
|
|
// Copy in z14_b2_c_prime
|
2021-12-01 18:10:00 -08:00
|
|
|
gate_cells.z14_b2_c_prime.copy_advice(
|
2021-07-24 11:41:54 -07:00
|
|
|
|| "z14_b2_c_prime",
|
2021-12-01 18:10:00 -08:00
|
|
|
&mut region,
|
2021-07-24 11:41:54 -07:00
|
|
|
self.advices[8],
|
2021-06-06 05:11:03 -07:00
|
|
|
offset,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cells used in the canonicity gate.
|
|
|
|
struct GateCells {
|
2021-12-01 18:51:46 -08:00
|
|
|
a: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
b: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
c: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
d: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
ak: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
nk: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
b_0: AssignedCell<pallas::Base, pallas::Base>,
|
2021-06-06 05:11:03 -07:00
|
|
|
b_1: Option<pallas::Base>,
|
2021-12-01 18:51:46 -08:00
|
|
|
b_2: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
d_0: AssignedCell<pallas::Base, pallas::Base>,
|
2021-06-06 05:11:03 -07:00
|
|
|
d_1: Option<pallas::Base>,
|
2021-12-01 18:51:46 -08:00
|
|
|
z13_a: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
a_prime: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
z13_a_prime: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
z13_c: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
b2_c_prime: AssignedCell<pallas::Base, pallas::Base>,
|
|
|
|
z14_b2_c_prime: AssignedCell<pallas::Base, pallas::Base>,
|
2021-06-06 05:11:03 -07:00
|
|
|
}
|
2021-06-22 06:47:27 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::CommitIvkConfig;
|
2022-01-28 07:53:03 -08:00
|
|
|
use crate::constants::{
|
|
|
|
fixed_bases::COMMIT_IVK_PERSONALIZATION, OrchardCommitDomains, OrchardFixedBases,
|
|
|
|
OrchardHashDomains, L_ORCHARD_BASE, T_Q,
|
2021-06-22 06:47:27 -07:00
|
|
|
};
|
2022-01-18 06:30:55 -08:00
|
|
|
use group::ff::{Field, PrimeFieldBits};
|
2022-01-28 07:53:03 -08:00
|
|
|
use halo2_gadgets::{
|
|
|
|
ecc::chip::{EccChip, EccConfig},
|
|
|
|
primitives::sinsemilla::CommitDomain,
|
|
|
|
sinsemilla::chip::SinsemillaChip,
|
|
|
|
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
|
|
|
|
};
|
|
|
|
use halo2_proofs::{
|
2021-12-01 18:51:46 -08:00
|
|
|
circuit::{AssignedCell, Layouter, SimpleFloorPlanner},
|
2021-06-22 06:47:27 -07:00
|
|
|
dev::MockProver,
|
|
|
|
plonk::{Circuit, ConstraintSystem, Error},
|
|
|
|
};
|
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
2022-01-18 06:30:55 -08:00
|
|
|
use rand::rngs::OsRng;
|
2021-06-22 06:47:27 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn commit_ivk() {
|
|
|
|
#[derive(Default)]
|
|
|
|
struct MyCircuit {
|
|
|
|
ak: Option<pallas::Base>,
|
|
|
|
nk: Option<pallas::Base>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
|
2021-12-01 18:51:46 -08:00
|
|
|
type Var = AssignedCell<pallas::Base, pallas::Base>;
|
2021-06-22 06:47:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Circuit<pallas::Base> for MyCircuit {
|
2021-08-18 23:59:39 -07:00
|
|
|
type Config = (CommitIvkConfig, EccConfig<OrchardFixedBases>);
|
2021-06-22 06:47:27 -07:00
|
|
|
type FloorPlanner = SimpleFloorPlanner;
|
|
|
|
|
|
|
|
fn without_witnesses(&self) -> Self {
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
|
|
|
|
let advices = [
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
meta.advice_column(),
|
|
|
|
];
|
|
|
|
|
2021-07-20 01:22:08 -07:00
|
|
|
let constants = meta.fixed_column();
|
|
|
|
meta.enable_constant(constants);
|
2021-06-22 06:47:27 -07:00
|
|
|
|
|
|
|
for advice in advices.iter() {
|
2022-01-04 21:28:16 -08:00
|
|
|
meta.enable_equality(*advice);
|
2021-06-22 06:47:27 -07:00
|
|
|
}
|
|
|
|
|
2021-07-27 10:32:32 -07:00
|
|
|
let table_idx = meta.lookup_table_column();
|
|
|
|
let lookup = (
|
|
|
|
table_idx,
|
|
|
|
meta.lookup_table_column(),
|
|
|
|
meta.lookup_table_column(),
|
|
|
|
);
|
2021-07-21 04:13:47 -07:00
|
|
|
let lagrange_coeffs = [
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
meta.fixed_column(),
|
|
|
|
];
|
2021-06-22 06:47:27 -07:00
|
|
|
|
2021-07-21 07:59:08 -07:00
|
|
|
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
|
2021-08-18 23:59:39 -07:00
|
|
|
let sinsemilla_config = SinsemillaChip::<
|
|
|
|
OrchardHashDomains,
|
|
|
|
OrchardCommitDomains,
|
|
|
|
OrchardFixedBases,
|
|
|
|
>::configure(
|
2021-07-21 07:59:08 -07:00
|
|
|
meta,
|
|
|
|
advices[..5].try_into().unwrap(),
|
2021-07-21 08:47:49 -07:00
|
|
|
advices[2],
|
2021-07-23 05:25:02 -07:00
|
|
|
lagrange_coeffs[0],
|
2021-07-21 07:59:08 -07:00
|
|
|
lookup,
|
2021-11-30 11:31:42 -08:00
|
|
|
range_check,
|
2021-07-21 07:59:08 -07:00
|
|
|
);
|
|
|
|
|
2021-06-22 06:47:27 -07:00
|
|
|
let commit_ivk_config =
|
|
|
|
CommitIvkConfig::configure(meta, advices, sinsemilla_config);
|
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
let ecc_config = EccChip::<OrchardFixedBases>::configure(
|
|
|
|
meta,
|
|
|
|
advices,
|
|
|
|
lagrange_coeffs,
|
|
|
|
range_check,
|
|
|
|
);
|
2021-06-22 06:47:27 -07:00
|
|
|
|
|
|
|
(commit_ivk_config, ecc_config)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn synthesize(
|
|
|
|
&self,
|
|
|
|
config: Self::Config,
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let (commit_ivk_config, ecc_config) = config;
|
|
|
|
|
|
|
|
// Load the Sinsemilla generator lookup table used by the whole circuit.
|
2021-08-18 23:59:39 -07:00
|
|
|
SinsemillaChip::<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>::load(commit_ivk_config.sinsemilla_config.clone(), &mut layouter)?;
|
2021-06-22 06:47:27 -07:00
|
|
|
|
|
|
|
// Construct a Sinsemilla chip
|
|
|
|
let sinsemilla_chip =
|
|
|
|
SinsemillaChip::construct(commit_ivk_config.sinsemilla_config.clone());
|
|
|
|
|
|
|
|
// Construct an ECC chip
|
|
|
|
let ecc_chip = EccChip::construct(ecc_config);
|
|
|
|
|
|
|
|
// Witness ak
|
|
|
|
let ak = self.load_private(
|
|
|
|
layouter.namespace(|| "load ak"),
|
|
|
|
commit_ivk_config.advices[0],
|
|
|
|
self.ak,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Witness nk
|
|
|
|
let nk = self.load_private(
|
|
|
|
layouter.namespace(|| "load nk"),
|
|
|
|
commit_ivk_config.advices[0],
|
|
|
|
self.nk,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Use a random scalar for rivk
|
2022-01-18 06:30:55 -08:00
|
|
|
let rivk = pallas::Scalar::random(OsRng);
|
2021-06-22 06:47:27 -07:00
|
|
|
|
2021-07-27 00:33:13 -07:00
|
|
|
let ivk = commit_ivk_config.assign_region(
|
2021-06-22 06:47:27 -07:00
|
|
|
sinsemilla_chip,
|
|
|
|
ecc_chip,
|
|
|
|
layouter.namespace(|| "CommitIvk"),
|
|
|
|
ak,
|
|
|
|
nk,
|
2021-07-27 00:33:13 -07:00
|
|
|
Some(rivk),
|
2021-06-22 06:47:27 -07:00
|
|
|
)?;
|
|
|
|
|
2021-07-27 00:33:13 -07:00
|
|
|
let expected_ivk = {
|
|
|
|
let domain = CommitDomain::new(COMMIT_IVK_PERSONALIZATION);
|
|
|
|
// Hash ak || nk
|
|
|
|
domain
|
|
|
|
.short_commit(
|
|
|
|
std::iter::empty()
|
|
|
|
.chain(
|
|
|
|
self.ak
|
|
|
|
.unwrap()
|
|
|
|
.to_le_bits()
|
|
|
|
.iter()
|
|
|
|
.by_val()
|
|
|
|
.take(L_ORCHARD_BASE),
|
|
|
|
)
|
|
|
|
.chain(
|
|
|
|
self.nk
|
|
|
|
.unwrap()
|
|
|
|
.to_le_bits()
|
|
|
|
.iter()
|
|
|
|
.by_val()
|
|
|
|
.take(L_ORCHARD_BASE),
|
|
|
|
),
|
|
|
|
&rivk,
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2021-12-01 16:10:00 -08:00
|
|
|
assert_eq!(&expected_ivk, ivk.inner().value().unwrap());
|
2021-07-27 00:33:13 -07:00
|
|
|
|
2021-06-22 06:47:27 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let two_pow_254 = pallas::Base::from_u128(1 << 127).square();
|
|
|
|
// Test different values of `ak`, `nk`
|
|
|
|
let circuits = [
|
|
|
|
// `ak` = 0, `nk` = 0
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(pallas::Base::zero()),
|
|
|
|
nk: Some(pallas::Base::zero()),
|
|
|
|
},
|
|
|
|
// `ak` = T_Q - 1, `nk` = T_Q - 1
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(pallas::Base::from_u128(T_Q - 1)),
|
|
|
|
nk: Some(pallas::Base::from_u128(T_Q - 1)),
|
|
|
|
},
|
|
|
|
// `ak` = T_Q, `nk` = T_Q
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(pallas::Base::from_u128(T_Q)),
|
|
|
|
nk: Some(pallas::Base::from_u128(T_Q)),
|
|
|
|
},
|
|
|
|
// `ak` = 2^127 - 1, `nk` = 2^127 - 1
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(pallas::Base::from_u128((1 << 127) - 1)),
|
|
|
|
nk: Some(pallas::Base::from_u128((1 << 127) - 1)),
|
|
|
|
},
|
|
|
|
// `ak` = 2^127, `nk` = 2^127
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(pallas::Base::from_u128(1 << 127)),
|
|
|
|
nk: Some(pallas::Base::from_u128(1 << 127)),
|
|
|
|
},
|
|
|
|
// `ak` = 2^254 - 1, `nk` = 2^254 - 1
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(two_pow_254 - pallas::Base::one()),
|
|
|
|
nk: Some(two_pow_254 - pallas::Base::one()),
|
|
|
|
},
|
|
|
|
// `ak` = 2^254, `nk` = 2^254
|
|
|
|
MyCircuit {
|
|
|
|
ak: Some(two_pow_254),
|
|
|
|
nk: Some(two_pow_254),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
for circuit in circuits.iter() {
|
|
|
|
let prover = MockProver::<pallas::Base>::run(11, circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|