2022-01-28 07:38:22 -08:00
|
|
|
//! Chip implementing a Merkle hash using Sinsemilla as the hash function.
|
|
|
|
|
2022-01-27 15:28:02 -08:00
|
|
|
use halo2_proofs::{
|
2021-12-01 18:51:46 -08:00
|
|
|
circuit::{AssignedCell, Chip, Layouter},
|
2022-04-24 15:13:38 -07:00
|
|
|
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Selector},
|
2021-06-04 09:54:10 -07:00
|
|
|
poly::Rotation,
|
|
|
|
};
|
|
|
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
|
|
|
|
2022-01-26 16:13:49 -08:00
|
|
|
use super::MerkleInstructions;
|
2021-06-04 09:54:10 -07:00
|
|
|
|
|
|
|
use crate::{
|
2022-05-08 19:54:04 -07:00
|
|
|
sinsemilla::{primitives as sinsemilla, MessagePiece},
|
2022-05-05 13:56:53 -07:00
|
|
|
utilities::RangeConstrained,
|
2022-01-27 13:53:10 -08:00
|
|
|
{
|
2021-08-18 23:59:39 -07:00
|
|
|
ecc::FixedPoints,
|
|
|
|
sinsemilla::{
|
|
|
|
chip::{SinsemillaChip, SinsemillaConfig},
|
|
|
|
CommitDomains, HashDomains, SinsemillaInstructions,
|
|
|
|
},
|
|
|
|
utilities::{
|
|
|
|
cond_swap::{CondSwapChip, CondSwapConfig, CondSwapInstructions},
|
|
|
|
UtilitiesInstructions,
|
|
|
|
},
|
2021-06-04 09:54:10 -07:00
|
|
|
},
|
|
|
|
};
|
2021-11-29 12:39:41 -08:00
|
|
|
use group::ff::PrimeField;
|
2021-06-04 09:54:10 -07:00
|
|
|
|
2022-01-28 07:38:22 -08:00
|
|
|
/// Configuration for the `MerkleChip` implementation.
|
2022-05-05 13:56:53 -07:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
2021-08-18 23:59:39 -07:00
|
|
|
pub struct MerkleConfig<Hash, Commit, Fixed>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
Fixed: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
|
|
|
{
|
2021-06-04 09:54:10 -07:00
|
|
|
advices: [Column<Advice>; 5],
|
2021-07-23 09:02:48 -07:00
|
|
|
q_decompose: Selector,
|
2021-06-04 09:54:10 -07:00
|
|
|
pub(super) cond_swap_config: CondSwapConfig,
|
2021-08-18 23:59:39 -07:00
|
|
|
pub(super) sinsemilla_config: SinsemillaConfig<Hash, Commit, Fixed>,
|
2021-06-04 09:54:10 -07:00
|
|
|
}
|
|
|
|
|
2022-01-28 07:38:22 -08:00
|
|
|
/// Chip implementing `MerkleInstructions`.
|
2022-05-05 20:17:52 -07:00
|
|
|
///
|
|
|
|
/// This chip specifically implements `MerkleInstructions::hash_layer` as the `MerkleCRH`
|
|
|
|
/// function `hash = SinsemillaHash(Q, 𝑙⋆ || left⋆ || right⋆)`, where:
|
|
|
|
/// - `𝑙⋆ = I2LEBSP_10(l)`
|
|
|
|
/// - `left⋆ = I2LEBSP_255(left)`
|
|
|
|
/// - `right⋆ = I2LEBSP_255(right)`
|
|
|
|
///
|
|
|
|
/// This chip does **NOT** constrain `left⋆` and `right⋆` to be canonical encodings of
|
|
|
|
/// `left` and `right`.
|
2022-05-05 13:56:53 -07:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
2021-08-18 23:59:39 -07:00
|
|
|
pub struct MerkleChip<Hash, Commit, Fixed>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
Fixed: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
|
|
|
{
|
|
|
|
config: MerkleConfig<Hash, Commit, Fixed>,
|
2021-06-04 09:54:10 -07:00
|
|
|
}
|
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
impl<Hash, Commit, Fixed> Chip<pallas::Base> for MerkleChip<Hash, Commit, Fixed>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
Fixed: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
|
|
|
|
{
|
|
|
|
type Config = MerkleConfig<Hash, Commit, Fixed>;
|
2021-06-04 09:54:10 -07:00
|
|
|
type Loaded = ();
|
|
|
|
|
|
|
|
fn config(&self) -> &Self::Config {
|
|
|
|
&self.config
|
|
|
|
}
|
|
|
|
|
|
|
|
fn loaded(&self) -> &Self::Loaded {
|
|
|
|
&()
|
|
|
|
}
|
|
|
|
}
|
2021-06-04 09:57:29 -07:00
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
impl<Hash, Commit, F> MerkleChip<Hash, Commit, F>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
F: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
|
|
|
{
|
2022-01-28 07:38:22 -08:00
|
|
|
/// Configures the [`MerkleChip`].
|
2021-06-04 09:58:11 -07:00
|
|
|
pub fn configure(
|
|
|
|
meta: &mut ConstraintSystem<pallas::Base>,
|
2021-08-18 23:59:39 -07:00
|
|
|
sinsemilla_config: SinsemillaConfig<Hash, Commit, F>,
|
|
|
|
) -> MerkleConfig<Hash, Commit, F> {
|
2021-07-19 05:56:18 -07:00
|
|
|
// All five advice columns are equality-enabled by SinsemillaConfig.
|
2021-06-04 09:58:11 -07:00
|
|
|
let advices = sinsemilla_config.advices();
|
2021-07-15 04:52:15 -07:00
|
|
|
let cond_swap_config = CondSwapChip::configure(meta, advices);
|
2021-06-04 09:58:11 -07:00
|
|
|
|
2021-07-23 09:02:48 -07:00
|
|
|
// This selector enables the decomposition gate.
|
|
|
|
let q_decompose = meta.selector();
|
2021-06-04 09:58:11 -07:00
|
|
|
|
|
|
|
// Check that pieces have been decomposed correctly for Sinsemilla hash.
|
2021-06-28 18:39:37 -07:00
|
|
|
// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
|
2021-06-04 09:58:11 -07:00
|
|
|
//
|
2021-07-23 09:36:50 -07:00
|
|
|
// a = a_0||a_1 = l || (bits 0..=239 of left)
|
2021-06-04 09:58:11 -07:00
|
|
|
// 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.
|
|
|
|
//
|
2022-05-05 20:17:52 -07:00
|
|
|
// The pieces and subpieces are arranged in the following configuration:
|
|
|
|
// | A_0 | A_1 | A_2 | A_3 | A_4 | q_decompose |
|
|
|
|
// -------------------------------------------------------
|
|
|
|
// | a | b | c | left | right | 1 |
|
|
|
|
// | z1_a | z1_b | b_1 | b_2 | l | 0 |
|
2021-06-04 09:58:11 -07:00
|
|
|
meta.create_gate("Decomposition check", |meta| {
|
2021-07-23 09:02:48 -07:00
|
|
|
let q_decompose = meta.query_selector(q_decompose);
|
2021-07-23 09:36:50 -07:00
|
|
|
let l_whole = meta.query_advice(advices[4], Rotation::next());
|
2021-06-04 09:58:11 -07:00
|
|
|
|
2021-12-07 09:47:03 -08:00
|
|
|
let two_pow_5 = pallas::Base::from(1 << 5);
|
2021-06-04 09:58:11 -07:00
|
|
|
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());
|
|
|
|
|
2021-07-23 09:36:50 -07:00
|
|
|
// a = a_0||a_1 = l || (bits 0..=239 of left)
|
2021-06-04 09:58:11 -07:00
|
|
|
//
|
|
|
|
// z_1 of SinsemillaHash(a) = a_1
|
2022-05-05 20:17:52 -07:00
|
|
|
// => a_0 = a - (a_1 * 2^10)
|
2021-06-04 09:58:11 -07:00
|
|
|
let z1_a = meta.query_advice(advices[0], Rotation::next());
|
|
|
|
let a_1 = z1_a;
|
2022-05-05 20:17:52 -07:00
|
|
|
// Derive a_0 (constrained by SinsemillaHash to be 10 bits)
|
|
|
|
let a_0 = a_whole - a_1.clone() * two_pow_10;
|
2021-06-04 09:58:11 -07:00
|
|
|
|
|
|
|
// b = b_0||b_1||b_2
|
|
|
|
// = (bits 240..=249 of left) || (bits 250..=254 of left) || (bits 0..=4 of right)
|
2021-06-28 18:39:37 -07:00
|
|
|
// The Orchard specification allows this representation to be non-canonical.
|
|
|
|
// <https://zips.z.cash/protocol/protocol.pdf#merklepath>
|
2021-06-04 09:58:11 -07:00
|
|
|
//
|
|
|
|
// 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());
|
2021-06-25 05:22:51 -07:00
|
|
|
// Constrain b_1 + 2^5 b_2 = z1_b
|
2022-05-08 20:27:03 -07:00
|
|
|
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-bit-lengths?partial
|
2021-06-25 05:22:51 -07:00
|
|
|
let b1_b2_check = z1_b.clone() - (b_1.clone() + b_2.clone() * two_pow_5);
|
2021-06-04 09:58:11 -07:00
|
|
|
// 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)
|
2022-05-08 20:27:03 -07:00
|
|
|
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-decomposition?partial
|
2021-06-04 09:58:11 -07:00
|
|
|
let left_check = {
|
|
|
|
let reconstructed = {
|
|
|
|
let two_pow_240 = pallas::Base::from_u128(1 << 120).square();
|
2021-06-28 18:39:37 -07:00
|
|
|
a_1 + (b_0 + b_1 * two_pow_10) * two_pow_240
|
2021-06-04 09:58:11 -07:00
|
|
|
};
|
|
|
|
reconstructed - left_node
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check that right = b_2 (5 bits) || c (250 bits)
|
2021-06-28 18:39:37 -07:00
|
|
|
// The Orchard specification allows this representation to be non-canonical.
|
|
|
|
// <https://zips.z.cash/protocol/protocol.pdf#merklepath>
|
2022-05-08 20:27:03 -07:00
|
|
|
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-decomposition?partial
|
2021-06-04 09:58:11 -07:00
|
|
|
let right_check = b_2 + c_whole * two_pow_5 - right_node;
|
|
|
|
|
2022-04-24 15:13:38 -07:00
|
|
|
Constraints::with_selector(
|
|
|
|
q_decompose,
|
2022-04-27 05:17:36 -07:00
|
|
|
[
|
2022-05-05 20:17:52 -07:00
|
|
|
("l_check", a_0 - l_whole),
|
2022-04-24 15:13:38 -07:00
|
|
|
("left_check", left_check),
|
|
|
|
("right_check", right_check),
|
|
|
|
("b1_b2_check", b1_b2_check),
|
2022-04-27 05:17:36 -07:00
|
|
|
],
|
2022-04-24 15:13:38 -07:00
|
|
|
)
|
2021-06-04 09:58:11 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
MerkleConfig {
|
|
|
|
advices,
|
2021-07-23 09:02:48 -07:00
|
|
|
q_decompose,
|
2021-06-04 09:58:11 -07:00
|
|
|
cond_swap_config,
|
|
|
|
sinsemilla_config,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 07:38:22 -08:00
|
|
|
/// Constructs a [`MerkleChip`] given a [`MerkleConfig`].
|
2021-08-18 23:59:39 -07:00
|
|
|
pub fn construct(config: MerkleConfig<Hash, Commit, F>) -> Self {
|
2021-06-04 09:58:11 -07:00
|
|
|
MerkleChip { config }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 16:13:49 -08:00
|
|
|
impl<Hash, Commit, F, const MERKLE_DEPTH: usize>
|
2021-11-29 12:39:41 -08:00
|
|
|
MerkleInstructions<pallas::Affine, MERKLE_DEPTH, { sinsemilla::K }, { sinsemilla::C }>
|
2021-08-18 23:59:39 -07:00
|
|
|
for MerkleChip<Hash, Commit, F>
|
|
|
|
where
|
2022-05-05 13:56:53 -07:00
|
|
|
Hash: HashDomains<pallas::Affine> + Eq,
|
2021-08-18 23:59:39 -07:00
|
|
|
F: FixedPoints<pallas::Affine>,
|
2022-05-05 13:56:53 -07:00
|
|
|
Commit: CommitDomains<pallas::Affine, F, Hash> + Eq,
|
2021-06-05 01:50:11 -07:00
|
|
|
{
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
fn hash_layer(
|
|
|
|
&self,
|
|
|
|
mut layouter: impl Layouter<pallas::Base>,
|
|
|
|
Q: pallas::Affine,
|
2021-11-29 12:39:41 -08:00
|
|
|
// l = MERKLE_DEPTH - layer - 1
|
2021-06-28 18:39:37 -07:00
|
|
|
l: usize,
|
|
|
|
left: Self::Var,
|
|
|
|
right: Self::Var,
|
|
|
|
) -> Result<Self::Var, Error> {
|
2021-06-05 01:50:11 -07:00
|
|
|
let config = self.config().clone();
|
|
|
|
|
2021-07-23 09:36:50 -07:00
|
|
|
// We need to hash `l || left || right`, where `l` is a 10-bit value.
|
2021-06-05 01:50:11 -07:00
|
|
|
// We allow `left` and `right` to be non-canonical 255-bit encodings.
|
|
|
|
//
|
2021-07-23 09:36:50 -07:00
|
|
|
// a = a_0||a_1 = l || (bits 0..=239 of left)
|
2021-06-05 01:50:11 -07:00
|
|
|
// 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
|
2022-05-05 20:17:52 -07:00
|
|
|
//
|
|
|
|
// We start by witnessing all of the individual pieces, and range-constraining the
|
|
|
|
// short pieces b_1 and b_2.
|
2022-05-08 20:27:03 -07:00
|
|
|
//
|
|
|
|
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-bit-lengths?partial
|
2021-06-05 01:50:11 -07:00
|
|
|
|
2021-07-23 09:36:50 -07:00
|
|
|
// `a = a_0||a_1` = `l` || (bits 0..=239 of `left`)
|
2022-05-05 13:56:53 -07:00
|
|
|
let a = MessagePiece::from_subpieces(
|
|
|
|
self.clone(),
|
|
|
|
layouter.namespace(|| "Witness a = a_0 || a_1"),
|
|
|
|
[
|
|
|
|
RangeConstrained::bitrange_of(Some(&pallas::Base::from(l as u64)), 0..10),
|
|
|
|
RangeConstrained::bitrange_of(left.value(), 0..240),
|
|
|
|
],
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
2021-06-28 18:39:37 -07:00
|
|
|
// b = b_0 || b_1 || b_2
|
2021-06-05 01:50:11 -07:00
|
|
|
// = (bits 240..=249 of left) || (bits 250..=254 of left) || (bits 0..=4 of right)
|
|
|
|
let (b_1, b_2, b) = {
|
|
|
|
// b_0 = (bits 240..=249 of `left`)
|
2022-05-05 13:56:53 -07:00
|
|
|
let b_0 = RangeConstrained::bitrange_of(left.value(), 240..250);
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
// b_1 = (bits 250..=254 of `left`)
|
|
|
|
// Constrain b_1 to 5 bits.
|
2022-05-05 13:56:53 -07:00
|
|
|
let b_1 = RangeConstrained::witness_short(
|
|
|
|
&config.sinsemilla_config.lookup_config(),
|
|
|
|
layouter.namespace(|| "b_1"),
|
|
|
|
left.value(),
|
|
|
|
250..(pallas::Base::NUM_BITS as usize),
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
// b_2 = (bits 0..=4 of `right`)
|
|
|
|
// Constrain b_2 to 5 bits.
|
2022-05-05 13:56:53 -07:00
|
|
|
let b_2 = RangeConstrained::witness_short(
|
|
|
|
&config.sinsemilla_config.lookup_config(),
|
|
|
|
layouter.namespace(|| "b_2"),
|
|
|
|
right.value(),
|
|
|
|
0..5,
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
2022-05-05 13:56:53 -07:00
|
|
|
let b = MessagePiece::from_subpieces(
|
|
|
|
self.clone(),
|
|
|
|
layouter.namespace(|| "Witness b = b_0 || b_1 || b_2"),
|
|
|
|
[b_0, b_1.value(), b_2.value()],
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
(b_1, b_2, b)
|
|
|
|
};
|
|
|
|
|
2022-05-05 13:56:53 -07:00
|
|
|
// c = bits 5..=254 of `right`
|
|
|
|
let c = MessagePiece::from_subpieces(
|
|
|
|
self.clone(),
|
|
|
|
layouter.namespace(|| "Witness c"),
|
|
|
|
[RangeConstrained::bitrange_of(
|
|
|
|
right.value(),
|
|
|
|
5..(pallas::Base::NUM_BITS as usize),
|
|
|
|
)],
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
2022-05-05 20:17:52 -07:00
|
|
|
// hash = SinsemillaHash(Q, 𝑙⋆ || left⋆ || right⋆)
|
|
|
|
//
|
|
|
|
// `hash = ⊥` is handled internally to `SinsemillaChip::hash_to_point`: incomplete
|
|
|
|
// addition constraints allows ⊥ to occur, and then during synthesis it detects
|
|
|
|
// these edge cases and raises an error (aborting proof creation).
|
|
|
|
//
|
|
|
|
// Note that MerkleCRH as-defined maps ⊥ to 0. This is for completeness outside
|
|
|
|
// the circuit (so that the ⊥ does not propagate into the type system). The chip
|
|
|
|
// explicitly doesn't map ⊥ to 0; in fact it cannot, as doing so would require
|
|
|
|
// constraints that amount to using complete addition. The rationale for excluding
|
|
|
|
// this map is the same as why Sinsemilla uses incomplete addition: this situation
|
|
|
|
// yields a nontrivial discrete log relation, and by assumption it is hard to find
|
|
|
|
// these.
|
2022-05-08 20:27:03 -07:00
|
|
|
//
|
|
|
|
// https://p.z.cash/proto:merkle-crh-orchard
|
2021-06-05 01:50:11 -07:00
|
|
|
let (point, zs) = self.hash_to_point(
|
2021-06-28 18:39:37 -07:00
|
|
|
layouter.namespace(|| format!("hash at l = {}", l)),
|
2021-06-05 01:50:11 -07:00
|
|
|
Q,
|
2022-05-05 13:56:53 -07:00
|
|
|
vec![a.inner(), b.inner(), c.inner()].into(),
|
2021-06-05 01:50:11 -07:00
|
|
|
)?;
|
2022-05-05 20:17:52 -07:00
|
|
|
let hash = Self::extract(&point);
|
|
|
|
|
|
|
|
// `SinsemillaChip::hash_to_point` returns the running sum for each `MessagePiece`.
|
|
|
|
// Grab the outputs we need for the decomposition constraints.
|
2021-12-01 04:51:33 -08:00
|
|
|
let z1_a = zs[0][1].clone();
|
|
|
|
let z1_b = zs[1][1].clone();
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
// Check that the pieces have been decomposed properly.
|
2022-05-05 20:17:52 -07:00
|
|
|
//
|
|
|
|
// The pieces and subpieces are arranged in the following configuration:
|
|
|
|
// | A_0 | A_1 | A_2 | A_3 | A_4 | q_decompose |
|
|
|
|
// -------------------------------------------------------
|
|
|
|
// | a | b | c | left | right | 1 |
|
2022-05-10 14:35:52 -07:00
|
|
|
// | z1_a | z1_b | b_1 | b_2 | l | 0 |
|
2021-06-05 01:50:11 -07:00
|
|
|
{
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "Check piece decomposition",
|
|
|
|
|mut region| {
|
2021-07-23 09:36:50 -07:00
|
|
|
// Set the fixed column `l` to the current l.
|
2021-11-29 12:39:41 -08:00
|
|
|
// Recall that l = MERKLE_DEPTH - layer - 1.
|
2021-06-28 18:39:37 -07:00
|
|
|
// The layer with 2^n nodes is called "layer n".
|
2021-07-23 09:02:48 -07:00
|
|
|
config.q_decompose.enable(&mut region, 0)?;
|
|
|
|
region.assign_advice_from_constant(
|
2021-07-23 09:36:50 -07:00
|
|
|
|| format!("l {}", l),
|
2021-07-23 09:02:48 -07:00
|
|
|
config.advices[4],
|
|
|
|
1,
|
2021-12-07 09:47:03 -08:00
|
|
|
pallas::Base::from(l as u64),
|
2021-06-05 01:50:11 -07:00
|
|
|
)?;
|
|
|
|
|
|
|
|
// Offset 0
|
|
|
|
// Copy and assign `a` at the correct position.
|
2022-05-05 13:56:53 -07:00
|
|
|
a.inner().cell_value().copy_advice(
|
|
|
|
|| "copy a",
|
|
|
|
&mut region,
|
|
|
|
config.advices[0],
|
|
|
|
0,
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy and assign `b` at the correct position.
|
2022-05-05 13:56:53 -07:00
|
|
|
b.inner().cell_value().copy_advice(
|
|
|
|
|| "copy b",
|
|
|
|
&mut region,
|
|
|
|
config.advices[1],
|
|
|
|
0,
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy and assign `c` at the correct position.
|
2022-05-05 13:56:53 -07:00
|
|
|
c.inner().cell_value().copy_advice(
|
|
|
|
|| "copy c",
|
|
|
|
&mut region,
|
|
|
|
config.advices[2],
|
|
|
|
0,
|
|
|
|
)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy and assign the left node at the correct position.
|
2021-12-01 18:10:00 -08:00
|
|
|
left.copy_advice(|| "left", &mut region, config.advices[3], 0)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy and assign the right node at the correct position.
|
2021-12-01 18:10:00 -08:00
|
|
|
right.copy_advice(|| "right", &mut region, config.advices[4], 0)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
// Offset 1
|
|
|
|
// Copy and assign z_1 of SinsemillaHash(a) = a_1
|
2021-12-01 18:10:00 -08:00
|
|
|
z1_a.copy_advice(|| "z1_a", &mut region, config.advices[0], 1)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy and assign z_1 of SinsemillaHash(b) = b_1
|
2021-12-01 18:10:00 -08:00
|
|
|
z1_b.copy_advice(|| "z1_b", &mut region, config.advices[1], 1)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy `b_1`, which has been constrained to be a 5-bit value
|
2022-05-05 13:56:53 -07:00
|
|
|
b_1.inner()
|
|
|
|
.copy_advice(|| "b_1", &mut region, config.advices[2], 1)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
// Copy `b_2`, which has been constrained to be a 5-bit value
|
2022-05-05 13:56:53 -07:00
|
|
|
b_2.inner()
|
|
|
|
.copy_advice(|| "b_2", &mut region, config.advices[3], 1)?;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check layer hash output against Sinsemilla primitives hash
|
|
|
|
#[cfg(test)]
|
|
|
|
{
|
2022-05-08 19:54:04 -07:00
|
|
|
use crate::{sinsemilla::primitives::HashDomain, utilities::i2lebsp};
|
|
|
|
|
2021-11-29 12:39:41 -08:00
|
|
|
use group::ff::PrimeFieldBits;
|
2021-06-05 01:50:11 -07:00
|
|
|
|
|
|
|
if let (Some(left), Some(right)) = (left.value(), right.value()) {
|
2021-07-23 09:36:50 -07:00
|
|
|
let l = i2lebsp::<10>(l as u64);
|
2021-06-05 01:50:11 -07:00
|
|
|
let left: Vec<_> = left
|
|
|
|
.to_le_bits()
|
|
|
|
.iter()
|
2022-05-04 16:36:18 -07:00
|
|
|
.by_vals()
|
2021-11-29 12:39:41 -08:00
|
|
|
.take(pallas::Base::NUM_BITS as usize)
|
2021-06-05 01:50:11 -07:00
|
|
|
.collect();
|
|
|
|
let right: Vec<_> = right
|
|
|
|
.to_le_bits()
|
|
|
|
.iter()
|
2022-05-04 16:36:18 -07:00
|
|
|
.by_vals()
|
2021-11-29 12:39:41 -08:00
|
|
|
.take(pallas::Base::NUM_BITS as usize)
|
2021-06-05 01:50:11 -07:00
|
|
|
.collect();
|
2022-01-27 13:15:41 -08:00
|
|
|
let merkle_crh = HashDomain::from_Q(Q.into());
|
2021-06-05 01:50:11 -07:00
|
|
|
|
2021-07-23 09:36:50 -07:00
|
|
|
let mut message = l.to_vec();
|
2021-06-05 01:50:11 -07:00
|
|
|
message.extend_from_slice(&left);
|
|
|
|
message.extend_from_slice(&right);
|
|
|
|
|
|
|
|
let expected = merkle_crh.hash(message.into_iter()).unwrap();
|
|
|
|
|
2022-05-05 20:17:52 -07:00
|
|
|
assert_eq!(expected.to_repr(), hash.value().unwrap().to_repr());
|
2021-06-05 01:50:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 20:17:52 -07:00
|
|
|
Ok(hash)
|
2021-06-05 01:50:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
impl<Hash, Commit, F> UtilitiesInstructions<pallas::Base> for MerkleChip<Hash, Commit, F>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
F: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
|
|
|
{
|
2021-12-01 18:51:46 -08:00
|
|
|
type Var = AssignedCell<pallas::Base, pallas::Base>;
|
2021-06-04 09:57:29 -07:00
|
|
|
}
|
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
impl<Hash, Commit, F> CondSwapInstructions<pallas::Base> for MerkleChip<Hash, Commit, F>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
F: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
|
|
|
{
|
2021-06-04 09:57:29 -07:00
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
fn swap(
|
|
|
|
&self,
|
|
|
|
layouter: impl Layouter<pallas::Base>,
|
|
|
|
pair: (Self::Var, Option<pallas::Base>),
|
|
|
|
swap: Option<bool>,
|
|
|
|
) -> Result<(Self::Var, Self::Var), Error> {
|
|
|
|
let config = self.config().cond_swap_config.clone();
|
|
|
|
let chip = CondSwapChip::<pallas::Base>::construct(config);
|
|
|
|
chip.swap(layouter, pair, swap)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
impl<Hash, Commit, F> SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }>
|
|
|
|
for MerkleChip<Hash, Commit, F>
|
|
|
|
where
|
|
|
|
Hash: HashDomains<pallas::Affine>,
|
|
|
|
F: FixedPoints<pallas::Affine>,
|
|
|
|
Commit: CommitDomains<pallas::Affine, F, Hash>,
|
|
|
|
{
|
|
|
|
type CellValue = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-04 09:57:29 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::CellValue;
|
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
type Message = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-04 09:57:29 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::Message;
|
2021-08-18 23:59:39 -07:00
|
|
|
type MessagePiece = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-04 09:57:29 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::MessagePiece;
|
2021-08-18 23:59:39 -07:00
|
|
|
type RunningSum = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-15 06:19:26 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::RunningSum;
|
2021-06-04 09:57:29 -07:00
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
type X = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-04 09:57:29 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::X;
|
2021-08-18 23:59:39 -07:00
|
|
|
type NonIdentityPoint = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-04 09:57:29 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
2021-09-27 07:33:57 -07:00
|
|
|
>>::NonIdentityPoint;
|
2021-08-18 23:59:39 -07:00
|
|
|
type FixedPoints = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-05 00:49:55 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::FixedPoints;
|
2021-06-04 09:57:29 -07:00
|
|
|
|
2021-08-18 23:59:39 -07:00
|
|
|
type HashDomains = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-04 09:57:29 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::HashDomains;
|
2021-08-18 23:59:39 -07:00
|
|
|
type CommitDomains = <SinsemillaChip<Hash, Commit, F> as SinsemillaInstructions<
|
2021-06-05 00:49:55 -07:00
|
|
|
pallas::Affine,
|
|
|
|
{ sinsemilla::K },
|
|
|
|
{ sinsemilla::C },
|
|
|
|
>>::CommitDomains;
|
2021-06-04 09:57:29 -07:00
|
|
|
|
|
|
|
fn witness_message_piece(
|
|
|
|
&self,
|
|
|
|
layouter: impl Layouter<pallas::Base>,
|
|
|
|
value: Option<pallas::Base>,
|
|
|
|
num_words: usize,
|
|
|
|
) -> Result<Self::MessagePiece, Error> {
|
|
|
|
let config = self.config().sinsemilla_config.clone();
|
2021-08-18 23:59:39 -07:00
|
|
|
let chip = SinsemillaChip::<Hash, Commit, F>::construct(config);
|
2021-06-04 09:57:29 -07:00
|
|
|
chip.witness_message_piece(layouter, value, num_words)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
fn hash_to_point(
|
|
|
|
&self,
|
|
|
|
layouter: impl Layouter<pallas::Base>,
|
|
|
|
Q: pallas::Affine,
|
|
|
|
message: Self::Message,
|
2021-09-27 07:33:57 -07:00
|
|
|
) -> Result<(Self::NonIdentityPoint, Vec<Vec<Self::CellValue>>), Error> {
|
2021-06-04 09:57:29 -07:00
|
|
|
let config = self.config().sinsemilla_config.clone();
|
2021-08-18 23:59:39 -07:00
|
|
|
let chip = SinsemillaChip::<Hash, Commit, F>::construct(config);
|
2021-06-04 09:57:29 -07:00
|
|
|
chip.hash_to_point(layouter, Q, message)
|
|
|
|
}
|
|
|
|
|
2021-09-27 07:33:57 -07:00
|
|
|
fn extract(point: &Self::NonIdentityPoint) -> Self::X {
|
2021-08-18 23:59:39 -07:00
|
|
|
SinsemillaChip::<Hash, Commit, F>::extract(point)
|
2021-06-04 09:57:29 -07:00
|
|
|
}
|
|
|
|
}
|