2019-08-20 15:54:11 -07:00
|
|
|
use crate::jubjub::*;
|
2019-08-20 16:17:21 -07:00
|
|
|
use ff::{Field, PrimeField, PrimeFieldRepr};
|
2017-12-28 10:06:05 -08:00
|
|
|
|
2018-07-25 13:46:00 -07:00
|
|
|
#[derive(Copy, Clone)]
|
2018-03-05 14:25:40 -08:00
|
|
|
pub enum Personalization {
|
|
|
|
NoteCommitment,
|
2019-08-15 09:39:55 -07:00
|
|
|
MerkleTree(usize),
|
2018-03-05 14:25:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Personalization {
|
|
|
|
pub fn get_bits(&self) -> Vec<bool> {
|
|
|
|
match *self {
|
2019-08-15 09:39:55 -07:00
|
|
|
Personalization::NoteCommitment => vec![true, true, true, true, true, true],
|
2018-03-05 18:21:41 -08:00
|
|
|
Personalization::MerkleTree(num) => {
|
|
|
|
assert!(num < 63);
|
|
|
|
|
2018-03-05 16:27:55 -08:00
|
|
|
(0..6).map(|i| (num >> i) & 1 == 1).collect()
|
2018-03-05 18:21:41 -08:00
|
|
|
}
|
2018-03-05 14:25:40 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-20 12:16:41 -08:00
|
|
|
|
2017-12-28 10:06:05 -08:00
|
|
|
pub fn pedersen_hash<E, I>(
|
2018-02-20 12:16:41 -08:00
|
|
|
personalization: Personalization,
|
2017-12-28 10:06:05 -08:00
|
|
|
bits: I,
|
2019-08-15 09:39:55 -07:00
|
|
|
params: &E::Params,
|
2017-12-28 10:06:05 -08:00
|
|
|
) -> edwards::Point<E, PrimeOrder>
|
2019-08-15 09:39:55 -07:00
|
|
|
where
|
|
|
|
I: IntoIterator<Item = bool>,
|
|
|
|
E: JubjubEngine,
|
2017-12-28 10:06:05 -08:00
|
|
|
{
|
2019-08-15 09:39:55 -07:00
|
|
|
let mut bits = personalization
|
|
|
|
.get_bits()
|
|
|
|
.into_iter()
|
|
|
|
.chain(bits.into_iter());
|
2017-12-28 10:06:05 -08:00
|
|
|
|
|
|
|
let mut result = edwards::Point::zero();
|
2018-07-25 13:46:00 -07:00
|
|
|
let mut generators = params.pedersen_hash_exp_table().iter();
|
2017-12-28 10:06:05 -08:00
|
|
|
|
|
|
|
loop {
|
|
|
|
let mut acc = E::Fs::zero();
|
|
|
|
let mut cur = E::Fs::one();
|
|
|
|
let mut chunks_remaining = params.pedersen_hash_chunks_per_generator();
|
|
|
|
let mut encountered_bits = false;
|
|
|
|
|
|
|
|
// Grab three bits from the input
|
|
|
|
while let Some(a) = bits.next() {
|
|
|
|
encountered_bits = true;
|
|
|
|
|
|
|
|
let b = bits.next().unwrap_or(false);
|
|
|
|
let c = bits.next().unwrap_or(false);
|
|
|
|
|
|
|
|
// Start computing this portion of the scalar
|
|
|
|
let mut tmp = cur;
|
|
|
|
if a {
|
|
|
|
tmp.add_assign(&cur);
|
|
|
|
}
|
|
|
|
cur.double(); // 2^1 * cur
|
|
|
|
if b {
|
|
|
|
tmp.add_assign(&cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
// conditionally negate
|
|
|
|
if c {
|
|
|
|
tmp.negate();
|
|
|
|
}
|
|
|
|
|
|
|
|
acc.add_assign(&tmp);
|
|
|
|
|
|
|
|
chunks_remaining -= 1;
|
|
|
|
|
|
|
|
if chunks_remaining == 0 {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
cur.double(); // 2^2 * cur
|
|
|
|
cur.double(); // 2^3 * cur
|
|
|
|
cur.double(); // 2^4 * cur
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !encountered_bits {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-15 09:39:55 -07:00
|
|
|
let mut table: &[Vec<edwards::Point<E, _>>] =
|
|
|
|
&generators.next().expect("we don't have enough generators");
|
2018-07-25 13:46:00 -07:00
|
|
|
let window = JubjubBls12::pedersen_hash_exp_window_size();
|
|
|
|
let window_mask = (1 << window) - 1;
|
|
|
|
|
|
|
|
let mut acc = acc.into_repr();
|
2019-08-15 09:39:55 -07:00
|
|
|
|
2018-07-25 13:46:00 -07:00
|
|
|
let mut tmp = edwards::Point::zero();
|
|
|
|
|
|
|
|
while !acc.is_zero() {
|
|
|
|
let i = (acc.as_ref()[0] & window_mask) as usize;
|
|
|
|
|
|
|
|
tmp = tmp.add(&table[0][i], params);
|
|
|
|
|
|
|
|
acc.shr(window);
|
|
|
|
table = &table[1..];
|
|
|
|
}
|
|
|
|
|
2017-12-28 10:06:05 -08:00
|
|
|
result = result.add(&tmp, params);
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
2018-07-31 01:32:31 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
|
|
|
|
use super::*;
|
2018-07-31 12:42:53 -07:00
|
|
|
use pairing::bls12_381::Bls12;
|
2018-07-31 01:32:31 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pedersen_hash_points() {
|
2018-07-31 12:42:53 -07:00
|
|
|
// Test vectors from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_pedersen.py
|
|
|
|
struct TestVector<'a> {
|
|
|
|
personalization: Personalization,
|
|
|
|
input_bits: Vec<u8>,
|
|
|
|
hash_x: &'a str,
|
|
|
|
hash_y: &'a str,
|
|
|
|
}
|
|
|
|
|
|
|
|
let test_vectors = vec![
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1],
|
|
|
|
hash_x: "Fr(0x06b1187c11ca4fb4383b2e0d0dbbde3ad3617338b5029187ec65a5eaed5e4d0b)",
|
|
|
|
hash_y: "Fr(0x3ce70f536652f0dea496393a1e55c4e08b9d55508e16d11e5db40d4810cbc982)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 0],
|
|
|
|
hash_x: "Fr(0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a)",
|
|
|
|
hash_y: "Fr(0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 1],
|
|
|
|
hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)",
|
|
|
|
hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 0, 0],
|
|
|
|
hash_x: "Fr(0x2fc3bc454c337f71d4f04f86304262fcbfc9ecd808716b92fc42cbe6827f7f1a)",
|
|
|
|
hash_y: "Fr(0x46d0d25bf1a654eedc6a9b1e5af398925113959feac31b7a2c036ff9b9ec0638)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 0, 1],
|
|
|
|
hash_x: "Fr(0x21746acd049f2c54579d5bb9c106083b4bb48c8910a06565d1e39e46939ca497)",
|
|
|
|
hash_y: "Fr(0x2cb69ae2615cd02c6ad2d6e06c1a0c15d49d71051d2d702155fca07bbf2d574c)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 1, 0],
|
|
|
|
hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)",
|
|
|
|
hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 1, 1],
|
|
|
|
hash_x: "Fr(0x3e09bdeea175dd4acd2e106caf4a5194200af53ee3a5a71338c083093d83eba5)",
|
|
|
|
hash_y: "Fr(0x579f6f15508af07d0f1beb117beaffe99e115a7ee859d81ddaa91d1096a103df)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
hash_x: "Fr(0x208d2e11ee496eb3b37257d1b4a77907e4b21d6c46d5487fb52d5a5239587ea0)",
|
|
|
|
hash_y: "Fr(0x1eeeb47b858257b9b69d009779e38c63332e20220eb474ef9af868274132181f)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
|
|
hash_x: "Fr(0x683ffae48988d517301ba81fb2c294c16a35ed1bba6411bd17312294843f37e0)",
|
|
|
|
hash_y: "Fr(0x40f7897b86747a5a857c8bd434ce3c1079efac22ed650d7345e5da31addacaff)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::NoteCommitment,
|
|
|
|
input_bits: vec![
|
|
|
|
1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0,
|
|
|
|
1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
|
|
0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0,
|
|
|
|
0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
|
|
],
|
|
|
|
hash_x: "Fr(0x676f78fa89da7c64502f790a99dfe177756867006809a6f174dcb427b345cd7c)",
|
|
|
|
hash_y: "Fr(0x1a6994a999a0abf83afc6ec5fe0ee8c8336a171653218cbfdf269689d5cfd3aa)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1],
|
|
|
|
hash_x: "Fr(0x61f8e2cb8e945631677b450d5e5669bc6b5f2ec69b321ac550dbe74525d7ac9a)",
|
|
|
|
hash_y: "Fr(0x4e11951ab9c9400ee38a18bd98cdb9453f1f67141ee9d9bf0c1c157d4fb34f9a)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 0],
|
|
|
|
hash_x: "Fr(0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c)",
|
|
|
|
hash_y: "Fr(0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 1],
|
|
|
|
hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)",
|
|
|
|
hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 0, 0],
|
|
|
|
hash_x: "Fr(0x27fa1e296c37dde8448483ce5485c2604d1d830e53812246299773a02ecd519c)",
|
|
|
|
hash_y: "Fr(0x08e499113675202cb42b4b681a31430814edebd72c5bb3bc3bfedf91fb0605df)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 0, 1],
|
|
|
|
hash_x: "Fr(0x5b4032d49431e7bfa085e2bb49bfc060909272a66287b063784f1d11b28a60e9)",
|
|
|
|
hash_y: "Fr(0x4627da49652efea2637595426add6ad682a0c8821d423f04c26ef5788d35f7e3)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 1, 0],
|
|
|
|
hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)",
|
|
|
|
hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 1, 1],
|
|
|
|
hash_x: "Fr(0x099e74a82c9c1858ac40db1a85959b1362d82fdd6efb99a443829f83003b0190)",
|
|
|
|
hash_y: "Fr(0x0f76f53d026574ad77ab4c6cd2428b9d94d158a9fc0469aae47c7535ff107881)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
hash_x: "Fr(0x240da48e40637664bcf3582708491d19e28a50787ea40b0a336d61735782d10a)",
|
|
|
|
hash_y: "Fr(0x6e630ddf6e43ad5568c925a4935e8e099230af4b2e19fab7d92b7e953b4986c3)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
|
|
hash_x: "Fr(0x06a477addbbfdf2934e34bdf6e071cd1276beaed801cd1b660ddcceb161ca8c7)",
|
|
|
|
hash_y: "Fr(0x355d39425378e57f393b30423cbde3ff69198ebac2ccbbafb92e25613352b0e8)",
|
|
|
|
},
|
|
|
|
TestVector {
|
|
|
|
personalization: Personalization::MerkleTree(34),
|
|
|
|
input_bits: vec![
|
|
|
|
0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0,
|
|
|
|
1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0,
|
|
|
|
0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0,
|
|
|
|
0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
|
|
],
|
|
|
|
hash_x: "Fr(0x094a624c5aac3569ad85428bb939d391bb5766ff87c389eb4d84d42aeaabb906)",
|
|
|
|
hash_y: "Fr(0x2cf51a8699edc64b73aa962464d4eadf038821900f9409350dc3ea2ccf12e054)",
|
|
|
|
},
|
|
|
|
];
|
|
|
|
// End test vectors
|
2018-07-31 01:32:31 -07:00
|
|
|
|
|
|
|
let params = &JubjubBls12::new();
|
|
|
|
|
2018-07-31 12:42:53 -07:00
|
|
|
let v = &test_vectors[0];
|
|
|
|
let input_bools: Vec<bool> = v.input_bits.iter().map(|&i| i == 1).collect();
|
|
|
|
|
|
|
|
// The 6 bits prefix is handled separately
|
|
|
|
assert_eq!(v.personalization.get_bits(), &input_bools[..6]);
|
|
|
|
|
|
|
|
let (x, y) =
|
|
|
|
pedersen_hash::<Bls12, _>(v.personalization, input_bools.into_iter().skip(6), params)
|
|
|
|
.to_xy();
|
|
|
|
|
|
|
|
assert_eq!(x.to_string(), v.hash_x);
|
|
|
|
assert_eq!(y.to_string(), v.hash_y);
|
2018-07-31 01:32:31 -07:00
|
|
|
}
|
2018-07-31 12:42:53 -07:00
|
|
|
}
|