Update master

This commit is contained in:
Aditya Kulkarni
2020-05-16 21:44:34 -07:00
218 changed files with 28917 additions and 19673 deletions

View File

@@ -1,9 +1,15 @@
[package]
name = "zcash_primitives"
version = "0.0.0"
description = "Rust implementations of the Zcash primitives"
version = "0.2.0"
authors = [
"Jack Grigg <jack@z.cash>",
]
homepage = "https://github.com/zcash/librustzcash"
repository = "https://github.com/zcash/librustzcash"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
[dependencies]
aes = "0.3"
@@ -11,21 +17,30 @@ blake2b_simd = "0.5"
blake2s_simd = "0.5"
byteorder = "1"
crypto_api_chachapoly = "0.2.1"
ff = { path = "../ff" }
ff = { version = "0.6", path = "../ff" }
fpe = "0.2"
hex = "0.3"
lazy_static = "1"
pairing = { path = "../pairing" }
log = "0.4"
pairing = { version = "0.16", path = "../pairing" }
rand = "0.7"
rand_core = "0.5"
rand_os = "0.2"
rand_core = "0.5.1"
ripemd160 = { version = "0.8", optional = true }
secp256k1 = { version = "=0.15.0", optional = true }
sha2 = "0.8"
subtle = "2.2.1"
[dev-dependencies]
hex-literal = "0.1"
criterion = "0.3"
hex-literal = "0.2"
rand_xorshift = "0.2"
[features]
transparent-inputs = ["ripemd160", "secp256k1"]
[[bench]]
name = "pedersen_hash"
harness = false
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -6,7 +6,8 @@ This library contains Rust implementations of the Zcash primitives.
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@@ -1,19 +1,10 @@
#![feature(test)]
extern crate pairing;
extern crate rand_core;
extern crate rand_os;
extern crate test;
extern crate zcash_primitives;
use criterion::{criterion_group, criterion_main, Criterion};
use pairing::bls12_381::Bls12;
use rand_core::RngCore;
use rand_os::OsRng;
use rand_core::{OsRng, RngCore};
use zcash_primitives::jubjub::JubjubBls12;
use zcash_primitives::pedersen_hash::{pedersen_hash, Personalization};
#[bench]
fn bench_pedersen_hash(b: &mut test::Bencher) {
fn bench_pedersen_hash(c: &mut Criterion) {
let params = JubjubBls12::new();
let rng = &mut OsRng;
let bits = (0..510)
@@ -21,5 +12,10 @@ fn bench_pedersen_hash(b: &mut test::Bencher) {
.collect::<Vec<_>>();
let personalization = Personalization::MerkleTree(31);
b.iter(|| pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params));
c.bench_function("Pedersen hash", |b| {
b.iter(|| pedersen_hash::<Bls12, _>(personalization, bits.clone(), &params))
});
}
criterion_group!(benches, bench_pedersen_hash);
criterion_main!(benches);

View File

@@ -1,3 +1,5 @@
//! Structs and methods for handling Zcash block headers.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use hex;
use sha2::{Digest, Sha256};
@@ -5,14 +7,16 @@ use std::fmt;
use std::io::{self, Read, Write};
use std::ops::Deref;
use serialize::Vector;
use crate::serialize::Vector;
pub mod equihash;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlockHash(pub [u8; 32]);
impl fmt::Display for BlockHash {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let mut data = self.0.clone();
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = self.0;
data.reverse();
formatter.write_str(&hex::encode(data))
}

View File

@@ -0,0 +1,469 @@
//! Verification functions for the [Equihash] proof-of-work algorithm.
//!
//! [Equihash]: https://zips.z.cash/protocol/protocol.pdf#equihash
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams, State as Blake2bState};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use log::error;
use std::io::Cursor;
use std::mem::size_of;
struct Params {
n: u32,
k: u32,
}
#[derive(Clone)]
struct Node {
hash: Vec<u8>,
indices: Vec<u32>,
}
impl Params {
fn indices_per_hash_output(&self) -> u32 {
512 / self.n
}
fn hash_output(&self) -> u8 {
(self.indices_per_hash_output() * self.n / 8) as u8
}
fn collision_bit_length(&self) -> usize {
(self.n / (self.k + 1)) as usize
}
fn collision_byte_length(&self) -> usize {
(self.collision_bit_length() + 7) / 8
}
fn hash_length(&self) -> usize {
((self.k as usize) + 1) * self.collision_byte_length()
}
}
impl Node {
fn new(p: &Params, state: &Blake2bState, i: u32) -> Self {
let hash = generate_hash(state, i / p.indices_per_hash_output());
let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize;
let end = start + (p.n as usize) / 8;
Node {
hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0),
indices: vec![i],
}
}
fn from_children(a: Node, b: Node, trim: usize) -> Self {
let hash: Vec<_> = a
.hash
.iter()
.zip(b.hash.iter())
.skip(trim)
.map(|(a, b)| a ^ b)
.collect();
let indices = if a.indices_before(&b) {
let mut indices = a.indices;
indices.extend(b.indices.iter());
indices
} else {
let mut indices = b.indices;
indices.extend(a.indices.iter());
indices
};
Node { hash, indices }
}
fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self {
let hash: Vec<_> = a
.hash
.iter()
.zip(b.hash.iter())
.skip(trim)
.map(|(a, b)| a ^ b)
.collect();
let mut indices = Vec::with_capacity(a.indices.len() + b.indices.len());
if a.indices_before(b) {
indices.extend(a.indices.iter());
indices.extend(b.indices.iter());
} else {
indices.extend(b.indices.iter());
indices.extend(a.indices.iter());
}
Node { hash, indices }
}
fn indices_before(&self, other: &Node) -> bool {
// Indices are serialized in big-endian so that integer
// comparison is equivalent to array comparison
self.indices[0] < other.indices[0]
}
fn is_zero(&self, len: usize) -> bool {
self.hash.iter().take(len).all(|v| *v == 0)
}
}
fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState {
let mut personalization: Vec<u8> = Vec::from("ZcashPoW");
personalization.write_u32::<LittleEndian>(n).unwrap();
personalization.write_u32::<LittleEndian>(k).unwrap();
Blake2bParams::new()
.hash_length(digest_len as usize)
.personal(&personalization)
.to_state()
}
fn generate_hash(base_state: &Blake2bState, i: u32) -> Blake2bHash {
let mut lei = [0u8; 4];
(&mut lei[..]).write_u32::<LittleEndian>(i).unwrap();
let mut state = base_state.clone();
state.update(&lei);
state.finalize()
}
fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
assert!(bit_len >= 8);
assert!(8 * size_of::<u32>() >= 7 + bit_len);
let out_width = (bit_len + 7) / 8 + byte_pad;
let out_len = 8 * out_width * vin.len() / bit_len;
// Shortcut for parameters where expansion is a no-op
if out_len == vin.len() {
return vin.to_vec();
}
let mut vout: Vec<u8> = vec![0; out_len];
let bit_len_mask: u32 = (1 << bit_len) - 1;
// The acc_bits least-significant bits of acc_value represent a bit sequence
// in big-endian order.
let mut acc_bits = 0;
let mut acc_value: u32 = 0;
let mut j = 0;
for b in vin {
acc_value = (acc_value << 8) | u32::from(*b);
acc_bits += 8;
// When we have bit_len or more bits in the accumulator, write the next
// output element.
if acc_bits >= bit_len {
acc_bits -= bit_len;
for x in byte_pad..out_width {
vout[j + x] = ((
// Big-endian
acc_value >> (acc_bits + (8 * (out_width - x - 1)))
) & (
// Apply bit_len_mask across byte boundaries
(bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF
)) as u8;
}
j += out_width;
}
}
vout
}
fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec<u32> {
assert!(((c_bit_len + 1) + 7) / 8 <= size_of::<u32>());
let len_indices = 8 * size_of::<u32>() * minimal.len() / (c_bit_len + 1);
let byte_pad = size_of::<u32>() - ((c_bit_len + 1) + 7) / 8;
let mut csr = Cursor::new(expand_array(minimal, c_bit_len + 1, byte_pad));
let mut ret = Vec::with_capacity(len_indices);
// Big-endian so that lexicographic array comparison is equivalent to integer
// comparison
while let Ok(i) = csr.read_u32::<BigEndian>() {
ret.push(i);
}
ret
}
fn has_collision(a: &Node, b: &Node, len: usize) -> bool {
a.hash
.iter()
.zip(b.hash.iter())
.take(len)
.all(|(a, b)| a == b)
}
fn distinct_indices(a: &Node, b: &Node) -> bool {
for i in &(a.indices) {
for j in &(b.indices) {
if i == j {
return false;
}
}
}
true
}
fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool {
if !has_collision(a, b, p.collision_byte_length()) {
error!("Invalid solution: invalid collision length between StepRows");
false
} else if b.indices_before(a) {
error!("Invalid solution: Index tree incorrectly ordered");
false
} else if !distinct_indices(a, b) {
error!("Invalid solution: duplicate indices");
false
} else {
true
}
}
pub fn is_valid_solution_iterative(
n: u32,
k: u32,
input: &[u8],
nonce: &[u8],
indices: &[u32],
) -> bool {
let p = Params { n, k };
let mut state = initialise_state(p.n, p.k, p.hash_output());
state.update(input);
state.update(nonce);
let mut rows = Vec::new();
for i in indices {
rows.push(Node::new(&p, &state, *i));
}
let mut hash_len = p.hash_length();
while rows.len() > 1 {
let mut cur_rows = Vec::new();
for pair in rows.chunks(2) {
let a = &pair[0];
let b = &pair[1];
if !validate_subtrees(&p, a, b) {
return false;
}
cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length()));
}
rows = cur_rows;
hash_len -= p.collision_byte_length();
}
assert!(rows.len() == 1);
rows[0].is_zero(hash_len)
}
fn tree_validator(p: &Params, state: &Blake2bState, indices: &[u32]) -> Option<Node> {
if indices.len() > 1 {
let end = indices.len();
let mid = end / 2;
match (
tree_validator(p, state, &indices[0..mid]),
tree_validator(p, state, &indices[mid..end]),
) {
(Some(a), Some(b)) => {
if validate_subtrees(p, &a, &b) {
Some(Node::from_children(a, b, p.collision_byte_length()))
} else {
None
}
}
_ => None,
}
} else {
Some(Node::new(&p, &state, indices[0]))
}
}
pub fn is_valid_solution_recursive(
n: u32,
k: u32,
input: &[u8],
nonce: &[u8],
indices: &[u32],
) -> bool {
let p = Params { n, k };
let mut state = initialise_state(p.n, p.k, p.hash_output());
state.update(input);
state.update(nonce);
match tree_validator(&p, &state, indices) {
Some(root) => {
// Hashes were trimmed, so only need to check remaining length
root.is_zero(p.collision_byte_length())
}
None => false,
}
}
pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool {
let p = Params { n, k };
let indices = indices_from_minimal(soln, p.collision_bit_length());
// Recursive validation is faster
is_valid_solution_recursive(n, k, input, nonce, &indices)
}
#[cfg(test)]
mod tests {
use super::is_valid_solution_iterative;
use super::is_valid_solution_recursive;
fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], indices: &[u32]) -> bool {
let a = is_valid_solution_iterative(n, k, input, nonce, indices);
let b = is_valid_solution_recursive(n, k, input, nonce, indices);
assert!(a == b);
a
}
#[test]
fn equihash_test_cases() {
let input = b"block header";
let mut nonce = [0 as u8; 32];
let mut indices = vec![
976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, 129167,
25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473,
17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894,
];
assert!(is_valid_solution(96, 5, input, &nonce, &indices));
indices = vec![
1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026,
64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158,
25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667,
];
assert!(is_valid_solution(96, 5, input, &nonce, &indices));
indices = vec![
4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660,
724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660,
313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528,
902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178,
198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165,
1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408,
396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006,
1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143,
102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808,
443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692,
236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681,
1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495,
494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024,
861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111,
1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110,
1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036,
330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542,
1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133,
953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265,
1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192,
123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459,
923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549,
575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951,
715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399,
97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822,
514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765,
122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683,
782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222,
817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076,
419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124,
883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041,
930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685,
60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414,
693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135,
897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231,
1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401,
327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838,
583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066,
1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392,
1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050,
317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140,
1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156,
767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421,
1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090,
84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342,
957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110,
1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128,
1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918,
380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551,
1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794,
981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044,
1971986,
];
assert!(!is_valid_solution(96, 5, input, &nonce, &indices));
assert!(is_valid_solution(200, 9, input, &nonce, &indices));
nonce[0] = 1;
assert!(!is_valid_solution(96, 5, input, &nonce, &indices));
assert!(!is_valid_solution(200, 9, input, &nonce, &indices));
indices = vec![
1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776,
27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337,
5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505,
];
assert!(is_valid_solution(96, 5, input, &nonce, &indices));
indices = vec![
1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496,
264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480,
214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, 1068055,
919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, 1117445, 1936058,
70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, 21678, 822781,
1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, 934427, 1068834,
629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, 1865216, 45723, 1820952,
1160970, 1585983, 422549, 1973097, 1296271, 2006382, 650084, 809838, 871727, 1080419,
28500, 1471829, 384406, 619459, 212041, 1466258, 481435, 866461, 145340, 1403843,
1339592, 1405761, 163425, 1073771, 285027, 1488210, 167744, 1182267, 1354059, 2089602,
921700, 2059931, 1704721, 1853088, 585171, 739246, 747551, 1520527, 590255, 1175747,
705292, 998433, 522014, 1931179, 1629531, 1692879, 588830, 1799457, 963672, 1664237,
775408, 1926741, 907030, 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487,
88704, 1302687, 579526, 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579,
1521894, 1917599, 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665,
1316156, 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721,
1565477, 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692,
1144365, 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179,
1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, 962900,
1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, 862432,
873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, 54984,
1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, 1403350,
2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, 456321,
834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, 1156906,
1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, 139460,
1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, 1514260,
2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, 1972064, 254647,
2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, 1768467, 853790,
1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, 920754, 1507358, 12883,
1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, 18523, 1236125, 669105,
1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, 985912, 2091029, 84065,
1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, 288960, 861994, 622074,
1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, 851053, 1715626, 531385,
1213667, 1093995, 1863757, 630365, 1851894, 1328101, 1770446, 31900, 734027, 1078651,
1701535, 123276, 1916343, 581822, 1681706, 573135, 818091, 1454710, 2052521, 1150284,
1451159, 1482280, 1811430, 26321, 785837, 877980, 2073103, 107324, 727248, 1785460,
1840517, 184560, 185640, 364103, 1878753, 518459, 1984029, 964109, 1884200, 74003,
527272, 516232, 711247, 148582, 209254, 634610, 1534140, 376714, 1573267, 421225,
1265101, 1078858, 1374310, 1806283, 2091298, 23392, 389637, 413663, 1066737, 226164,
762552, 1048220, 1583397, 40092, 277435, 775449, 1533894, 202582, 390703, 346741,
1027320, 523034, 809424, 584882, 1296934, 528062, 733331, 1212771, 1958651, 653372,
1313962, 1366332, 1784489, 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431,
1698486, 57289, 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060,
1285209, 265623, 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035,
1296171, 158847, 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101,
133251, 1136222, 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029,
1457135, 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019,
1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, 1137531,
1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, 1939366, 232907,
747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, 229793, 1576982, 1420059,
1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113,
1822645, 1082368, 1392894,
];
assert!(!is_valid_solution(96, 5, input, &nonce, &indices));
assert!(is_valid_solution(200, 9, input, &nonce, &indices));
let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.";
indices = vec![
2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080,
45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132,
23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568,
];
assert!(is_valid_solution(96, 5, input2, &nonce, &indices));
}
}

View File

@@ -0,0 +1,237 @@
//! Consensus parameters.
use std::convert::TryFrom;
use std::fmt;
/// Zcash consensus parameters.
pub trait Parameters {
fn activation_height(nu: NetworkUpgrade) -> Option<u32>;
fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool {
match Self::activation_height(nu) {
Some(h) if h <= height => true,
_ => false,
}
}
}
/// Marker struct for the production network.
#[derive(Clone, Copy, Debug)]
pub struct MainNetwork;
impl Parameters for MainNetwork {
fn activation_height(nu: NetworkUpgrade) -> Option<u32> {
match nu {
NetworkUpgrade::Overwinter => Some(347_500),
NetworkUpgrade::Sapling => Some(419_200),
NetworkUpgrade::Blossom => Some(653_600),
NetworkUpgrade::Heartwood => None,
}
}
}
/// Marker struct for the test network.
#[derive(Clone, Copy, Debug)]
pub struct TestNetwork;
impl Parameters for TestNetwork {
fn activation_height(nu: NetworkUpgrade) -> Option<u32> {
match nu {
NetworkUpgrade::Overwinter => Some(207_500),
NetworkUpgrade::Sapling => Some(280_000),
NetworkUpgrade::Blossom => Some(584_000),
NetworkUpgrade::Heartwood => None,
}
}
}
/// An event that occurs at a specified height on the Zcash chain, at which point the
/// consensus rules enforced by the network are altered.
///
/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details.
#[derive(Clone, Copy, Debug)]
pub enum NetworkUpgrade {
/// The [Overwinter] network upgrade.
///
/// [Overwinter]: https://z.cash/upgrade/overwinter/
Overwinter,
/// The [Sapling] network upgrade.
///
/// [Sapling]: https://z.cash/upgrade/sapling/
Sapling,
/// The [Blossom] network upgrade.
///
/// [Blossom]: https://z.cash/upgrade/blossom/
Blossom,
/// The [Heartwood] network upgrade.
///
/// [Heartwood]: https://z.cash/upgrade/heartwood/
Heartwood,
}
impl fmt::Display for NetworkUpgrade {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NetworkUpgrade::Overwinter => write!(f, "Overwinter"),
NetworkUpgrade::Sapling => write!(f, "Sapling"),
NetworkUpgrade::Blossom => write!(f, "Blossom"),
NetworkUpgrade::Heartwood => write!(f, "Heartwood"),
}
}
}
impl NetworkUpgrade {
fn branch_id(self) -> BranchId {
match self {
NetworkUpgrade::Overwinter => BranchId::Overwinter,
NetworkUpgrade::Sapling => BranchId::Sapling,
NetworkUpgrade::Blossom => BranchId::Blossom,
NetworkUpgrade::Heartwood => BranchId::Heartwood,
}
}
}
/// The network upgrades on the Zcash chain in order of activation.
///
/// This order corresponds to the activation heights, but because Rust enums are
/// full-fledged algebraic data types, we need to define it manually.
const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
NetworkUpgrade::Overwinter,
NetworkUpgrade::Sapling,
NetworkUpgrade::Blossom,
NetworkUpgrade::Heartwood,
];
/// A globally-unique identifier for a set of consensus rules within the Zcash chain.
///
/// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash
/// network upgrades. For example, `BranchId::Overwinter` corresponds to the blocks
/// starting at Overwinter activation, and ending the block before Sapling activation.
///
/// The main use of the branch ID is in signature generation: transactions commit to a
/// specific branch ID by including it as part of [`signature_hash`]. This ensures
/// two-way replay protection for transactions across network upgrades.
///
/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details.
///
/// [`signature_hash`]: crate::transaction::signature_hash
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BranchId {
/// The consensus rules at the launch of Zcash.
Sprout,
/// The consensus rules deployed by [`NetworkUpgrade::Overwinter`].
Overwinter,
/// The consensus rules deployed by [`NetworkUpgrade::Sapling`].
Sapling,
/// The consensus rules deployed by [`NetworkUpgrade::Blossom`].
Blossom,
/// The consensus rules deployed by [`NetworkUpgrade::Heartwood`].
Heartwood,
}
impl TryFrom<u32> for BranchId {
type Error = &'static str;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(BranchId::Sprout),
0x5ba8_1b19 => Ok(BranchId::Overwinter),
0x76b8_09bb => Ok(BranchId::Sapling),
0x2bb4_0e60 => Ok(BranchId::Blossom),
0xf5b9_230b => Ok(BranchId::Heartwood),
_ => Err("Unknown consensus branch ID"),
}
}
}
impl From<BranchId> for u32 {
fn from(consensus_branch_id: BranchId) -> u32 {
match consensus_branch_id {
BranchId::Sprout => 0,
BranchId::Overwinter => 0x5ba8_1b19,
BranchId::Sapling => 0x76b8_09bb,
BranchId::Blossom => 0x2bb4_0e60,
BranchId::Heartwood => 0xf5b9_230b,
}
}
}
impl BranchId {
/// Returns the branch ID corresponding to the consensus rule set that is active at
/// the given height.
///
/// This is the branch ID that should be used when creating transactions.
pub fn for_height<C: Parameters>(height: u32) -> Self {
for nu in UPGRADES_IN_ORDER.iter().rev() {
if C::is_nu_active(*nu, height) {
return nu.branch_id();
}
}
// Sprout rules apply before any network upgrade
BranchId::Sprout
}
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use super::{BranchId, MainNetwork, NetworkUpgrade, Parameters, UPGRADES_IN_ORDER};
#[test]
fn nu_ordering() {
for i in 1..UPGRADES_IN_ORDER.len() {
let nu_a = UPGRADES_IN_ORDER[i - 1];
let nu_b = UPGRADES_IN_ORDER[i];
match (
MainNetwork::activation_height(nu_a),
MainNetwork::activation_height(nu_b),
) {
(Some(a), Some(b)) if a < b => (),
(Some(_), None) => (),
(None, None) => (),
_ => panic!(
"{} should not be before {} in UPGRADES_IN_ORDER",
nu_a, nu_b
),
}
}
}
#[test]
fn nu_is_active() {
assert!(!MainNetwork::is_nu_active(NetworkUpgrade::Overwinter, 0));
assert!(!MainNetwork::is_nu_active(
NetworkUpgrade::Overwinter,
347_499
));
assert!(MainNetwork::is_nu_active(
NetworkUpgrade::Overwinter,
347_500
));
}
#[test]
fn branch_id_from_u32() {
assert_eq!(BranchId::try_from(0), Ok(BranchId::Sprout));
assert!(BranchId::try_from(1).is_err());
}
#[test]
fn branch_id_for_height() {
assert_eq!(BranchId::for_height::<MainNetwork>(0), BranchId::Sprout,);
assert_eq!(
BranchId::for_height::<MainNetwork>(419_199),
BranchId::Overwinter,
);
assert_eq!(
BranchId::for_height::<MainNetwork>(419_200),
BranchId::Sapling,
);
assert_eq!(
BranchId::for_height::<MainNetwork>(5_000_000),
BranchId::Blossom,
);
}
}

View File

@@ -1,32 +1,34 @@
//! Various constants used by the Zcash primitives.
/// First 64 bytes of the BLAKE2s input during group hash.
/// This is chosen to be some random string that we couldn't have anticipated when we designed
/// the algorithm, for rigidity purposes.
/// We deliberately use an ASCII hex string of 32 bytes here.
pub const GH_FIRST_BLOCK: &'static [u8; 64] =
pub const GH_FIRST_BLOCK: &[u8; 64] =
b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0";
// BLAKE2s invocation personalizations
/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk)
pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] = b"Zcashivk";
pub const CRH_IVK_PERSONALIZATION: &[u8; 8] = b"Zcashivk";
/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho)
pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] = b"Zcash_nf";
pub const PRF_NF_PERSONALIZATION: &[u8; 8] = b"Zcash_nf";
// Group hash personalizations
/// BLAKE2s Personalization for Pedersen hash generators.
pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] = b"Zcash_PH";
pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &[u8; 8] = b"Zcash_PH";
/// BLAKE2s Personalization for the group hash for key diversification
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] = b"Zcash_gd";
pub const KEY_DIVERSIFICATION_PERSONALIZATION: &[u8; 8] = b"Zcash_gd";
/// BLAKE2s Personalization for the spending key base point
pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_G_";
pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_G_";
/// BLAKE2s Personalization for the proof generation key base point
pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_H_";
pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_H_";
/// BLAKE2s Personalization for the value commitment generator for the value
pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_cv";
pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_cv";
/// BLAKE2s Personalization for the nullifier position generator (for computing rho)
pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] = b"Zcash_J_";
pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &[u8; 8] = b"Zcash_J_";

View File

@@ -1,9 +1,13 @@
use jubjub::{edwards, JubjubEngine, PrimeOrder};
//! Implementation of [group hashing into Jubjub][grouphash].
//!
//! [grouphash]: https://zips.z.cash/protocol/protocol.pdf#concretegrouphashjubjub
use crate::jubjub::{edwards, JubjubEngine, PrimeOrder};
use ff::PrimeField;
use crate::constants;
use blake2s_simd::Params;
use constants;
/// Produces a random point in the Jubjub curve.
/// The point is guaranteed to be prime order

View File

@@ -1,4 +1,6 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use ff::{BitIterator, Field, PrimeField};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
@@ -81,33 +83,36 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
}
impl<E: JubjubEngine> Point<E, Unknown> {
pub fn read<R: Read>(reader: R, params: &E::Params) -> io::Result<Self> {
pub fn read<R: Read>(mut reader: R, params: &E::Params) -> io::Result<Self> {
let mut y_repr = <E::Fr as PrimeField>::Repr::default();
y_repr.read_le(reader)?;
reader.read_exact(y_repr.as_mut())?;
let x_sign = (y_repr.as_ref()[3] >> 63) == 1;
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
let x_sign = (y_repr.as_ref()[31] >> 7) == 1;
y_repr.as_mut()[31] &= 0x7f;
match E::Fr::from_repr(y_repr) {
Ok(y) => match Self::get_for_y(y, x_sign, params) {
Some(p) => Ok(p),
None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")),
},
Err(_) => Err(io::Error::new(
Some(y) => {
let p = Self::get_for_y(y, x_sign, params);
if bool::from(p.is_some()) {
Ok(p.unwrap())
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
}
}
None => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"y is not in field",
)),
}
}
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> CtOption<Self> {
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
// This is defined for all valid y-coordinates,
// as dy^2 + 1 = 0 has no solution in Fr.
// tmp1 = y^2
let mut tmp1 = y;
tmp1.square();
let mut tmp1 = y.square();
// tmp2 = (y^2 * d) + 1
let mut tmp2 = tmp1;
@@ -117,33 +122,27 @@ impl<E: JubjubEngine> Point<E, Unknown> {
// tmp1 = y^2 - 1
tmp1.sub_assign(&E::Fr::one());
match tmp2.inverse() {
Some(tmp2) => {
// tmp1 = (y^2 - 1) / (dy^2 + 1)
tmp1.mul_assign(&tmp2);
tmp2.invert().and_then(|tmp2| {
// tmp1 = (y^2 - 1) / (dy^2 + 1)
tmp1.mul_assign(&tmp2);
match tmp1.sqrt() {
Some(mut x) => {
if x.into_repr().is_odd() != sign {
x.negate();
}
let mut t = x;
t.mul_assign(&y);
Some(Point {
x: x,
y: y,
t: t,
z: E::Fr::one(),
_marker: PhantomData,
})
}
None => None,
tmp1.sqrt().map(|mut x| {
if x.is_odd() != sign {
x = x.neg();
}
}
None => None,
}
let mut t = x;
t.mul_assign(&y);
Point {
x,
y,
t,
z: E::Fr::one(),
_marker: PhantomData,
}
})
})
}
/// This guarantees the point is in the prime order subgroup
@@ -159,31 +158,31 @@ impl<E: JubjubEngine> Point<E, Unknown> {
let y = E::Fr::random(rng);
let sign = rng.next_u32() % 2 != 0;
if let Some(p) = Self::get_for_y(y, sign, params) {
return p;
let p = Self::get_for_y(y, sign, params);
if bool::from(p.is_some()) {
return p.unwrap();
}
}
}
}
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
let (x, y) = self.into_xy();
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
let (x, y) = self.to_xy();
assert_eq!(E::Fr::NUM_BITS, 255);
let x_repr = x.into_repr();
let mut y_repr = y.into_repr();
if x_repr.is_odd() {
y_repr.as_mut()[3] |= 0x8000000000000000u64;
let mut y_repr = y.to_repr();
if x.is_odd() {
y_repr.as_mut()[31] |= 0x80;
}
y_repr.write_le(writer)
writer.write_all(y_repr.as_ref())
}
/// Convert from a Montgomery point
pub fn from_montgomery(m: &montgomery::Point<E, Subgroup>, params: &E::Params) -> Self {
match m.into_xy() {
match m.to_xy() {
None => {
// Map the point at infinity to the neutral element.
Point::zero()
@@ -212,12 +211,9 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
// only point of order 2 that is not the neutral element.
if y.is_zero() {
// This must be the point (0, 0) as above.
let mut neg1 = E::Fr::one();
neg1.negate();
Point {
x: E::Fr::zero(),
y: neg1,
y: E::Fr::one().neg(),
t: E::Fr::zero(),
z: E::Fr::one(),
_marker: PhantomData,
@@ -277,8 +273,8 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
Point {
x: u,
y: v,
t: t,
z: z,
t,
z,
_marker: PhantomData,
}
}
@@ -306,8 +302,9 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
}
}
pub fn into_xy(&self) -> (E::Fr, E::Fr) {
let zinv = self.z.inverse().unwrap();
/// Convert to affine coordinates
pub fn to_xy(&self) -> (E::Fr, E::Fr) {
let zinv = self.z.invert().unwrap();
let mut x = self.x;
x.mul_assign(&zinv);
@@ -322,8 +319,8 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn negate(&self) -> Self {
let mut p = self.clone();
p.x.negate();
p.t.negate();
p.x = p.x.neg();
p.t = p.t.neg();
p
}
@@ -336,27 +333,22 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// A = X1^2
let mut a = self.x;
a.square();
let a = self.x.square();
// B = Y1^2
let mut b = self.y;
b.square();
let b = self.y.square();
// C = 2*Z1^2
let mut c = self.z;
c.square();
c.double();
let c = self.z.square().double();
// D = a*A
// = -A
let mut d = a;
d.negate();
let d = a.neg();
// E = (X1+Y1)^2 - A - B
let mut e = self.x;
e.add_assign(&self.y);
e.square();
e = e.square();
e.add_assign(&d); // -A = D
e.sub_assign(&b);
@@ -412,7 +404,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
b.mul_assign(&other.y);
// C = d * t1 * t2
let mut c = params.edwards_d().clone();
let mut c = *params.edwards_d();
c.mul_assign(&self.t);
c.mul_assign(&other.t);
@@ -475,7 +467,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
let mut res = Self::zero();
for b in BitIterator::new(scalar.into()) {
for b in BitIterator::<u8, _>::new(scalar.into()) {
res = res.double(params);
if b {

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,6 @@
//! The [Jubjub] curve for efficient elliptic curve operations in circuits built
//! over [BLS12-381].
//!
//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar
//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with
//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery
@@ -16,13 +19,16 @@
//! It is a complete twisted Edwards curve, so the equivalence with
//! the Montgomery curve forms a group isomorphism, allowing points
//! to be freely converted between the two forms.
//!
//! [Jubjub]: https://zips.z.cash/protocol/protocol.pdf#jubjub
//! [BLS12-381]: pairing::bls12_381
use ff::{Field, PrimeField, SqrtField};
use ff::{Field, PrimeField};
use pairing::Engine;
use group_hash::group_hash;
use crate::group_hash::group_hash;
use constants;
use crate::constants;
use pairing::bls12_381::{Bls12, Fr};
@@ -89,7 +95,7 @@ pub trait ToUniform {
/// and some pre-computed parameters.
pub trait JubjubEngine: Engine {
/// The scalar field of the Jubjub curve
type Fs: PrimeField + SqrtField + ToUniform;
type Fs: PrimeField + ToUniform;
/// The parameters of Jubjub and the Sapling protocol
type Params: JubjubParams<Self>;
}
@@ -122,7 +128,7 @@ pub trait JubjubParams<E: JubjubEngine>: Sized {
fn generator(&self, base: FixedGenerators) -> &edwards::Point<E, PrimeOrder>;
/// Returns a window table [0, 1, ..., 8] for different magnitudes of some
/// fixed generator.
fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
fn circuit_generators(&self, _: FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
/// Returns the window size for exponentiation of Pedersen hash generators
/// outside the circuit
fn pedersen_hash_exp_window_size() -> u32;
@@ -189,8 +195,7 @@ impl JubjubParams<Bls12> for JubjubBls12 {
impl JubjubBls12 {
pub fn new() -> Self {
let montgomery_a = Fr::from_str("40962").unwrap();
let mut montgomery_2a = montgomery_a;
montgomery_2a.double();
let montgomery_2a = montgomery_a.double();
let mut tmp_params = JubjubBls12 {
// d = -(10240/10241)
@@ -199,9 +204,9 @@ impl JubjubBls12 {
)
.unwrap(),
// A = 40962
montgomery_a: montgomery_a,
montgomery_a,
// 2A = 2.A
montgomery_2a: montgomery_2a,
montgomery_2a,
// scaling factor = sqrt(4 / (a - d))
scale: Fr::from_str(
"17814886934372412843466061268024708274627479829237077604635722030778476050649",
@@ -216,33 +221,11 @@ impl JubjubBls12 {
fixed_base_circuit_generators: vec![],
};
fn find_group_hash<E: JubjubEngine>(
m: &[u8],
personalization: &[u8; 8],
params: &E::Params,
) -> edwards::Point<E, PrimeOrder> {
let mut tag = m.to_vec();
let i = tag.len();
tag.push(0u8);
loop {
let gh = group_hash(&tag, personalization, params);
// We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value());
tag[i] += 1;
if let Some(gh) = gh {
break gh;
}
}
}
// Create the bases for the Pedersen hashes
{
let mut pedersen_hash_generators = vec![];
for m in 0..5 {
for m in 0..6 {
use byteorder::{LittleEndian, WriteBytesExt};
let mut segment_number = [0u8; 4];
@@ -250,26 +233,17 @@ impl JubjubBls12 {
.write_u32::<LittleEndian>(m)
.unwrap();
pedersen_hash_generators.push(find_group_hash(
pedersen_hash_generators.push(JubjubBls12::find_group_hash(
&segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
));
}
// Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
panic!("Neutral element!");
}
for p2 in pedersen_hash_generators.iter().skip(i + 1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
}
}
JubjubBls12::check_consistency_of_pedersen_hash_generators(
&tmp_params,
&pedersen_hash_generators,
);
tmp_params.pedersen_hash_generators = pedersen_hash_generators;
}
@@ -314,43 +288,47 @@ impl JubjubBls12 {
let mut fixed_base_generators =
vec![edwards::Point::zero(); FixedGenerators::Max as usize];
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = find_group_hash(
&[],
constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
JubjubBls12::find_group_hash(
&[],
constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
find_group_hash(
JubjubBls12::find_group_hash(
b"r",
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] = find_group_hash(
&[],
constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
JubjubBls12::find_group_hash(
&[],
constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = find_group_hash(
b"v",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
JubjubBls12::find_group_hash(
b"v",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
find_group_hash(
JubjubBls12::find_group_hash(
b"r",
constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = find_group_hash(
&[],
constants::SPENDING_KEY_GENERATOR_PERSONALIZATION,
&tmp_params,
);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
JubjubBls12::find_group_hash(
&[],
constants::SPENDING_KEY_GENERATOR_PERSONALIZATION,
&tmp_params,
);
// Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in fixed_base_generators.iter().enumerate() {
@@ -374,7 +352,7 @@ impl JubjubBls12 {
let mut pedersen_circuit_generators = vec![];
// Process each segment
for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() {
for gen in tmp_params.pedersen_hash_generators.iter().cloned() {
let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
let mut windows = vec![];
for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
@@ -384,7 +362,7 @@ impl JubjubBls12 {
// coeffs = g, g*2, g*3, g*4
for _ in 0..4 {
coeffs.push(g.into_xy().expect("cannot produce O"));
coeffs.push(g.to_xy().expect("cannot produce O"));
g = g.add(&gen, &tmp_params);
}
windows.push(coeffs);
@@ -411,7 +389,7 @@ impl JubjubBls12 {
let mut coeffs = vec![(Fr::zero(), Fr::one())];
let mut g = gen.clone();
for _ in 0..7 {
coeffs.push(g.into_xy());
coeffs.push(g.to_xy());
g = g.add(&gen, &tmp_params);
}
windows.push(coeffs);
@@ -427,10 +405,71 @@ impl JubjubBls12 {
tmp_params
}
fn find_group_hash<E: JubjubEngine>(
m: &[u8],
personalization: &[u8; 8],
params: &E::Params,
) -> edwards::Point<E, PrimeOrder> {
let mut tag = m.to_vec();
let i = tag.len();
tag.push(0u8);
loop {
let gh = group_hash(&tag, personalization, params);
// We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value());
tag[i] += 1;
if let Some(gh) = gh {
break gh;
}
}
}
/// Check for simple relations between the generators, that make finding collisions easy;
/// far worse than spec inconsistencies!
fn check_consistency_of_pedersen_hash_generators<E: JubjubEngine>(
tmp_params: &E::Params,
pedersen_hash_generators: &[edwards::Point<E, PrimeOrder>],
) {
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
panic!("Neutral element!");
}
for p2 in pedersen_hash_generators.iter().skip(i + 1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
if p1 == &p2.negate() {
panic!("Inverse generator!");
}
}
// check for a generator being the sum of any other two
for (j, p2) in pedersen_hash_generators.iter().enumerate() {
if j == i {
continue;
}
for (k, p3) in pedersen_hash_generators.iter().enumerate() {
if k == j || k == i {
continue;
}
let sum = &p2.add(&p3, &tmp_params);
if sum == p1 {
panic!("Linear relation between generators!");
}
}
}
}
}
}
#[test]
fn test_jubjub_bls12() {
use hex_literal::hex;
let params = JubjubBls12::new();
tests::test_suite::<Bls12>(&params);
@@ -464,3 +503,35 @@ fn test_jubjub_bls12() {
assert!(p == q);
}
#[test]
#[should_panic(expected = "Linear relation between generators!")]
fn test_jubjub_bls12_pedersen_hash_generators_consistency_check_linear_relation() {
let params = JubjubBls12::new();
let mut pedersen_hash_generators: Vec<edwards::Point<Bls12, PrimeOrder>> = vec![];
use byteorder::{LittleEndian, WriteBytesExt};
for m in 0..5 {
let mut segment_number = [0u8; 4];
(&mut segment_number[0..4])
.write_u32::<LittleEndian>(m)
.unwrap();
let p = JubjubBls12::find_group_hash(
&segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&params,
);
pedersen_hash_generators.push(p);
}
let p1 = pedersen_hash_generators[0].clone();
let p2 = pedersen_hash_generators[1].clone();
//test for linear relation
pedersen_hash_generators.push(p1.add(&p2, &params));
JubjubBls12::check_consistency_of_pedersen_hash_generators(&params, &pedersen_hash_generators);
}

View File

@@ -1,4 +1,6 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use ff::{BitIterator, Field, PrimeField};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
@@ -46,11 +48,10 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
}
impl<E: JubjubEngine> Point<E, Unknown> {
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption<Self> {
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
let mut x2 = x;
x2.square();
let mut x2 = x.square();
let mut rhs = x2;
rhs.mul_assign(params.montgomery_a());
@@ -58,21 +59,18 @@ impl<E: JubjubEngine> Point<E, Unknown> {
x2.mul_assign(&x);
rhs.add_assign(&x2);
match rhs.sqrt() {
Some(mut y) => {
if y.into_repr().is_odd() != sign {
y.negate();
}
return Some(Point {
x: x,
y: y,
infinity: false,
_marker: PhantomData,
});
rhs.sqrt().map(|mut y| {
if y.is_odd() != sign {
y = y.neg();
}
None => None,
}
Point {
x,
y,
infinity: false,
_marker: PhantomData,
}
})
}
/// This guarantees the point is in the prime order subgroup
@@ -88,9 +86,9 @@ impl<E: JubjubEngine> Point<E, Unknown> {
let x = E::Fr::random(rng);
let sign = rng.next_u32() % 2 != 0;
match Self::get_for_x(x, sign, params) {
Some(p) => return p,
None => {}
let p = Self::get_for_x(x, sign, params);
if p.is_some().into() {
return p.unwrap();
}
}
}
@@ -99,7 +97,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
/// Convert from an Edwards point
pub fn from_edwards(e: &edwards::Point<E, Subgroup>, params: &E::Params) -> Self {
let (x, y) = e.into_xy();
let (x, y) = e.to_xy();
if y == E::Fr::one() {
// The only solution for y = 1 is x = 0. (0, 1) is
@@ -140,11 +138,11 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
{
let mut tmp = E::Fr::one();
tmp.sub_assign(&y);
u.mul_assign(&tmp.inverse().unwrap())
u.mul_assign(&tmp.invert().unwrap())
}
let mut v = u;
v.mul_assign(&x.inverse().unwrap());
v.mul_assign(&x.invert().unwrap());
// Scale it into the correct curve constants
v.mul_assign(params.scale());
@@ -178,7 +176,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
}
}
pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)> {
pub fn to_xy(&self) -> Option<(E::Fr, E::Fr)> {
if self.infinity {
None
} else {
@@ -190,7 +188,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn negate(&self) -> Self {
let mut p = self.clone();
p.y.negate();
p.y = p.y.neg();
p
}
@@ -214,26 +212,24 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
let mut delta = E::Fr::one();
{
let mut tmp = params.montgomery_a().clone();
let mut tmp = *params.montgomery_a();
tmp.mul_assign(&self.x);
tmp.double();
tmp = tmp.double();
delta.add_assign(&tmp);
}
{
let mut tmp = self.x;
tmp.square();
let mut tmp = self.x.square();
delta.add_assign(&tmp);
tmp.double();
tmp = tmp.double();
delta.add_assign(&tmp);
}
{
let mut tmp = self.y;
tmp.double();
delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero"));
let tmp = self.y.double();
// y is nonzero so this must be nonzero
delta.mul_assign(&tmp.invert().unwrap());
}
let mut x3 = delta;
x3.square();
let mut x3 = delta.square();
x3.sub_assign(params.montgomery_a());
x3.sub_assign(&self.x);
x3.sub_assign(&self.x);
@@ -242,7 +238,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y3.sub_assign(&self.x);
y3.mul_assign(&delta);
y3.add_assign(&self.y);
y3.negate();
y3 = y3.neg();
Point {
x: x3,
@@ -276,14 +272,11 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
{
let mut tmp = other.x;
tmp.sub_assign(&self.x);
delta.mul_assign(
&tmp.inverse()
.expect("self.x != other.x, so this must be nonzero"),
);
// self.x != other.x, so this must be nonzero
delta.mul_assign(&tmp.invert().unwrap());
}
let mut x3 = delta;
x3.square();
let mut x3 = delta.square();
x3.sub_assign(params.montgomery_a());
x3.sub_assign(&self.x);
x3.sub_assign(&other.x);
@@ -292,7 +285,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
y3.sub_assign(&self.x);
y3.mul_assign(&delta);
y3.add_assign(&self.y);
y3.negate();
y3 = y3.neg();
Point {
x: x3,
@@ -311,7 +304,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
let mut res = Self::zero();
for b in BitIterator::new(scalar.into()) {
for b in BitIterator::<u8, _>::new(scalar.into()) {
res = res.double(params);
if b {

View File

@@ -1,6 +1,7 @@
use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder};
use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField};
use ff::{Endianness, Field, PrimeField};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
@@ -19,11 +20,9 @@ pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
}
fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(x: E::Fr, y: E::Fr, params: &P) -> bool {
let mut lhs = y;
lhs.square();
let lhs = y.square();
let mut x2 = x;
x2.square();
let x2 = x.square();
let mut x3 = x2;
x3.mul_assign(&x);
@@ -41,11 +40,9 @@ fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
y: E::Fr,
params: &P,
) -> bool {
let mut x2 = x;
x2.square();
let x2 = x.square();
let mut y2 = y;
y2.square();
let y2 = y.square();
// -x^2 + y^2
let mut lhs = y2;
@@ -119,13 +116,13 @@ fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
assert!(res2 == res3);
assert!(res3 == res4);
let (x, y) = res1.into_xy();
let (x, y) = res1.to_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
let (x, y) = res2.into_xy();
let (x, y) = res2.to_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
let (x, y) = res3.into_xy();
let (x, y) = res3.to_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
}
}
@@ -237,8 +234,10 @@ fn test_get_for<E: JubjubEngine>(params: &E::Params) {
let y = E::Fr::random(rng);
let sign = rng.next_u32() % 2 == 1;
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) {
assert!(p.into_xy().0.into_repr().is_odd() == sign);
let p = edwards::Point::<E, _>::get_for_y(y, sign, params);
if bool::from(p.is_some()) {
let mut p = p.unwrap();
assert!(p.to_xy().0.is_odd() == sign);
p = p.negate();
assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p);
}
@@ -274,12 +273,12 @@ fn test_rand<E: JubjubEngine>(params: &E::Params) {
let e = edwards::Point::<E, _>::rand(rng, params);
{
let (x, y) = p.into_xy().unwrap();
let (x, y) = p.to_xy().unwrap();
assert!(is_on_mont_curve(x, y, params));
}
{
let (x, y) = e.into_xy();
let (x, y) = e.to_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
}
}
@@ -309,23 +308,19 @@ fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
// a = -1
let mut a = E::Fr::one();
a.negate();
let a = E::Fr::one().neg();
{
// Check that 2A is consistent with A
let mut tmp = *params.montgomery_a();
tmp.double();
assert_eq!(&tmp, params.montgomery_2a());
assert_eq!(&params.montgomery_a().double(), params.montgomery_2a());
}
{
// The twisted Edwards addition law is complete when d is nonsquare
// and a is square.
assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(a.legendre() == LegendreSymbol::QuadraticResidue);
assert!(bool::from(params.edwards_d().sqrt().is_none()));
assert!(bool::from(a.sqrt().is_some()));
}
{
@@ -335,38 +330,37 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
let mut tmp = *params.edwards_d();
// 1 / d is nonsquare
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(bool::from(tmp.invert().unwrap().sqrt().is_none()));
// tmp = -d
tmp.negate();
tmp = tmp.neg();
// -d is nonsquare
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(bool::from(tmp.sqrt().is_none()));
// 1 / -d is nonsquare
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(bool::from(tmp.invert().unwrap().sqrt().is_none()));
}
{
// Check that A^2 - 4 is nonsquare:
let mut tmp = params.montgomery_a().clone();
tmp.square();
let mut tmp = params.montgomery_a().square();
tmp.sub_assign(&E::Fr::from_str("4").unwrap());
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(bool::from(tmp.sqrt().is_none()));
}
{
// Check that A - 2 is nonsquare:
let mut tmp = params.montgomery_a().clone();
tmp.sub_assign(&E::Fr::from_str("2").unwrap());
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(bool::from(tmp.sqrt().is_none()));
}
{
// Check the validity of the scaling factor
let mut tmp = a;
tmp.sub_assign(&params.edwards_d());
tmp = tmp.inverse().unwrap();
tmp = tmp.invert().unwrap();
tmp.mul_assign(&E::Fr::from_str("4").unwrap());
tmp = tmp.sqrt().unwrap();
assert_eq!(&tmp, params.scale());
@@ -376,32 +370,55 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
// Check that the number of windows per generator
// in the Pedersen hash does not allow for collisions
let mut cur = E::Fs::one().into_repr();
let mut cur = E::Fs::one();
let mut max = E::Fs::char();
{
max.sub_noborrow(&E::Fs::one().into_repr());
max.div2();
}
let max = {
// Grab char - 1 in little endian.
let mut tmp = (-E::Fs::one()).to_repr();
<E::Fs as PrimeField>::ReprEndianness::toggle_little_endian(&mut tmp);
let mut pacc = E::Fs::zero().into_repr();
let mut nacc = E::Fs::char();
// Shift right by 1 bit.
let mut borrow = 0;
for b in tmp.as_mut().iter_mut().rev() {
let new_borrow = *b & 1;
*b = (borrow << 7) | (*b >> 1);
borrow = new_borrow;
}
// Turns out we want this in little endian!
tmp
};
let mut pacc = E::Fs::zero();
let mut nacc = E::Fs::zero();
for _ in 0..params.pedersen_hash_chunks_per_generator() {
// tmp = cur * 4
let mut tmp = cur;
tmp.mul2();
tmp.mul2();
let tmp = cur.double().double();
pacc.add_nocarry(&tmp);
nacc.sub_noborrow(&tmp);
pacc += &tmp;
nacc -= &tmp; // The first subtraction wraps intentionally.
assert!(pacc < max);
assert!(pacc < nacc);
let mut pacc_repr = pacc.to_repr();
let mut nacc_repr = nacc.to_repr();
<E::Fs as PrimeField>::ReprEndianness::toggle_little_endian(&mut pacc_repr);
<E::Fs as PrimeField>::ReprEndianness::toggle_little_endian(&mut nacc_repr);
fn less_than(val: &[u8], bound: &[u8]) -> bool {
for (a, b) in val.iter().rev().zip(bound.iter().rev()) {
if a < b {
return true;
}
}
false
}
assert!(less_than(pacc_repr.as_ref(), max.as_ref()));
assert!(less_than(pacc_repr.as_ref(), nacc_repr.as_ref()));
// cur = cur * 16
for _ in 0..4 {
cur.mul2();
cur = cur.double();
}
}
}

View File

@@ -1,20 +1,22 @@
//! Sapling key components.
//!
//! Implements section 4.2.2 of the Zcash Protocol Specification.
//! Implements [section 4.2.2] of the Zcash Protocol Specification.
//!
//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
use crate::{
jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown},
primitives::{ProofGenerationKey, ViewingKey},
};
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::{PrimeField, PrimeFieldRepr};
use ff::PrimeField;
use std::io::{self, Read, Write};
pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed";
pub const PRF_EXPAND_PERSONALIZATION: &[u8; 16] = b"Zcash_ExpandSeed";
/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)
pub fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bHash {
prf_expand_vec(sk, &vec![t])
prf_expand_vec(sk, &[t])
}
pub fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bHash {
@@ -69,14 +71,14 @@ impl<E: JubjubEngine> ExpandedSpendingKey<E> {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut ask_repr = <E::Fs as PrimeField>::Repr::default();
ask_repr.read_le(&mut reader)?;
reader.read_exact(ask_repr.as_mut())?;
let ask = E::Fs::from_repr(ask_repr)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "ask not in field"))?;
let mut nsk_repr = <E::Fs as PrimeField>::Repr::default();
nsk_repr.read_le(&mut reader)?;
reader.read_exact(nsk_repr.as_mut())?;
let nsk = E::Fs::from_repr(nsk_repr)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nsk not in field"))?;
let mut ovk = [0; 32];
reader.read_exact(&mut ovk)?;
@@ -89,8 +91,8 @@ impl<E: JubjubEngine> ExpandedSpendingKey<E> {
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.ask.into_repr().write_le(&mut writer)?;
self.nsk.into_repr().write_le(&mut writer)?;
writer.write_all(self.ask.to_repr().as_ref())?;
writer.write_all(self.nsk.to_repr().as_ref())?;
writer.write_all(&self.ovk.0)?;
Ok(())
@@ -111,7 +113,7 @@ impl<E: JubjubEngine> Clone for FullViewingKey<E> {
ak: self.vk.ak.clone(),
nk: self.vk.nk.clone(),
},
ovk: self.ovk.clone(),
ovk: self.ovk,
}
}
}

View File

@@ -1,19 +1,10 @@
#[macro_use]
extern crate lazy_static;
//! *General Zcash primitives.*
//!
//! `zcash_primitives` is a library that provides the core structs and functions necessary
//! for working with Zcash.
extern crate aes;
extern crate blake2b_simd;
extern crate blake2s_simd;
extern crate byteorder;
extern crate crypto_api_chachapoly;
extern crate ff;
extern crate fpe;
extern crate hex;
extern crate pairing;
extern crate rand;
extern crate rand_core;
extern crate rand_os;
extern crate sha2;
// Catch documentation errors caused by code changes.
#![deny(intra_doc_link_resolution_failure)]
#[cfg(feature = "transparent-inputs")]
extern crate ripemd160;
@@ -27,8 +18,10 @@ extern crate hex_literal;
#[cfg(test)]
extern crate rand_xorshift;
use lazy_static::lazy_static;
pub mod block;
pub mod consensus;
pub mod constants;
pub mod group_hash;
pub mod jubjub;
@@ -49,7 +42,7 @@ pub mod zip32;
#[cfg(test)]
mod test_vectors;
use jubjub::JubjubBls12;
use crate::jubjub::JubjubBls12;
lazy_static! {
pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };

View File

@@ -5,8 +5,8 @@ use std::collections::VecDeque;
use std::io::{self, Read, Write};
use std::iter;
use sapling::SAPLING_COMMITMENT_TREE_DEPTH;
use serialize::{Optional, Vector};
use crate::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
use crate::serialize::{Optional, Vector};
/// A hashable node within a Merkle tree.
pub trait Hashable: Clone + Copy {
@@ -17,13 +17,13 @@ pub trait Hashable: Clone + Copy {
fn write<W: Write>(&self, writer: W) -> io::Result<()>;
/// Returns the parent node within the tree of the two given nodes.
fn combine(usize, &Self, &Self) -> Self;
fn combine(_: usize, _: &Self, _: &Self) -> Self;
/// Returns a blank leaf node.
fn blank() -> Self;
/// Returns the empty root for the given depth.
fn empty_root(usize) -> Self;
fn empty_root(_: usize) -> Self;
}
struct PathFiller<Node: Hashable> {
@@ -164,14 +164,8 @@ impl<Node: Hashable> CommitmentTree<Node> {
// - Empty leaves are used as needed.
let leaf_root = Node::combine(
0,
&match self.left {
Some(node) => node,
None => filler.next(0),
},
&match self.right {
Some(node) => node,
None => filler.next(0),
},
&self.left.unwrap_or_else(|| filler.next(0)),
&self.right.unwrap_or_else(|| filler.next(0)),
);
// 2) Hash in parents up to the currently-filled depth.
@@ -202,27 +196,28 @@ impl<Node: Hashable> CommitmentTree<Node> {
/// ```
/// extern crate ff;
/// extern crate pairing;
/// extern crate rand_os;
/// extern crate rand_core;
/// extern crate zcash_primitives;
///
/// use ff::{Field, PrimeField};
/// use pairing::bls12_381::Fr;
/// use rand_os::OsRng;
/// use rand_core::OsRng;
/// use zcash_primitives::{
/// merkle_tree::{CommitmentTree, IncrementalWitness},
/// sapling::Node,
/// };
///
/// let mut rng = OsRng::new().unwrap();
/// let mut rng = OsRng;
///
/// let mut tree = CommitmentTree::<Node>::new();
///
/// tree.append(Node::new(Fr::random(&mut rng).into_repr()));
/// tree.append(Node::new(Fr::random(&mut rng).into_repr()));
/// tree.append(Node::new(Fr::random(&mut rng).to_repr()));
/// tree.append(Node::new(Fr::random(&mut rng).to_repr()));
/// let mut witness = IncrementalWitness::from_tree(&tree);
/// assert_eq!(witness.position(), 1);
/// assert_eq!(tree.root(), witness.root());
///
/// let cmu = Node::new(Fr::random(&mut rng).into_repr());
/// let cmu = Node::new(Fr::random(&mut rng).to_repr());
/// tree.append(cmu);
/// witness.append(cmu);
/// assert_eq!(tree.root(), witness.root());
@@ -380,19 +375,19 @@ impl<Node: Hashable> IncrementalWitness<Node> {
}
/// Returns the current witness, or None if the tree is empty.
pub fn path(&self) -> Option<CommitmentTreeWitness<Node>> {
pub fn path(&self) -> Option<MerklePath<Node>> {
self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH)
}
fn path_inner(&self, depth: usize) -> Option<CommitmentTreeWitness<Node>> {
fn path_inner(&self, depth: usize) -> Option<MerklePath<Node>> {
let mut filler = self.filler();
let mut auth_path = Vec::new();
if let Some(node) = self.tree.left {
if self.tree.right.is_some() {
auth_path.push(Some((node, true)));
auth_path.push((node, true));
} else {
auth_path.push(Some((filler.next(0), false)));
auth_path.push((filler.next(0), false));
}
} else {
// Can't create an authentication path for the beginning of the tree
@@ -401,41 +396,37 @@ impl<Node: Hashable> IncrementalWitness<Node> {
for (i, p) in self.tree.parents.iter().enumerate() {
auth_path.push(match p {
Some(node) => Some((*node, true)),
None => Some((filler.next(i + 1), false)),
Some(node) => (*node, true),
None => (filler.next(i + 1), false),
});
}
for i in self.tree.parents.len()..(depth - 1) {
auth_path.push(Some((filler.next(i + 1), false)));
auth_path.push((filler.next(i + 1), false));
}
assert_eq!(auth_path.len(), depth);
Some(CommitmentTreeWitness::from_path(
auth_path,
self.position() as u64,
))
Some(MerklePath::from_path(auth_path, self.position() as u64))
}
}
/// A witness to a path from a position in a particular commitment tree to the root of
/// that tree.
/// A path from a position in a particular commitment tree to the root of that tree.
#[derive(Clone, Debug, PartialEq)]
pub struct CommitmentTreeWitness<Node: Hashable> {
pub auth_path: Vec<Option<(Node, bool)>>,
pub struct MerklePath<Node: Hashable> {
pub auth_path: Vec<(Node, bool)>,
pub position: u64,
}
impl<Node: Hashable> CommitmentTreeWitness<Node> {
/// Constructs a witness directly from its path and position.
pub fn from_path(auth_path: Vec<Option<(Node, bool)>>, position: u64) -> Self {
CommitmentTreeWitness {
impl<Node: Hashable> MerklePath<Node> {
/// Constructs a Merkle path directly from a path and position.
pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self {
MerklePath {
auth_path,
position,
}
}
/// Reads a witness from its serialized form.
/// Reads a Merkle path from its serialized form.
pub fn from_slice(witness: &[u8]) -> Result<Self, ()> {
Self::from_slice_with_depth(witness, SAPLING_COMMITMENT_TREE_DEPTH)
}
@@ -449,46 +440,41 @@ impl<Node: Hashable> CommitmentTreeWitness<Node> {
witness = &witness[1..];
// Begin to construct the authentication path
let mut auth_path = vec![None; depth];
let iter = witness.chunks_exact(33);
witness = iter.remainder();
// The vector works in reverse
for i in (0..depth).rev() {
// skip length of inner vector
if witness[0] != 32 {
// the length of a pedersen hash
return Err(());
}
witness = &witness[1..];
// Grab the sibling node at this depth in the tree
let mut sibling = [0u8; 32];
sibling.copy_from_slice(&witness[0..32]);
witness = &witness[32..];
// Sibling node should be an element of Fr
let sibling = match Node::read(&sibling[..]) {
Ok(p) => p,
Err(_) => return Err(()),
};
// Set the value in the auth path; we put false here
// for now (signifying the position bit) which we'll
// fill in later.
auth_path[i] = Some((sibling, false));
let mut auth_path = iter
.rev()
.map(|bytes| {
// Length of inner vector should be the length of a Pedersen hash
if bytes[0] == 32 {
// Sibling node should be an element of Fr
Node::read(&bytes[1..])
.map(|sibling| {
// Set the value in the auth path; we put false here
// for now (signifying the position bit) which we'll
// fill in later.
(sibling, false)
})
.map_err(|_| ())
} else {
Err(())
}
})
.collect::<Result<Vec<_>, _>>()?;
if auth_path.len() != depth {
return Err(());
}
// Read the position from the witness
let position = match witness.read_u64::<LittleEndian>() {
Ok(pos) => pos,
Err(_) => return Err(()),
};
let position = witness.read_u64::<LittleEndian>().map_err(|_| ())?;
// Given the position, let's finish constructing the authentication
// path
let mut tmp = position;
for i in 0..depth {
auth_path[i].as_mut().map(|p| p.1 = (tmp & 1) == 1);
for entry in auth_path.iter_mut() {
entry.1 = (tmp & 1) == 1;
tmp >>= 1;
}
@@ -496,7 +482,7 @@ impl<Node: Hashable> CommitmentTreeWitness<Node> {
// have provided more information than they should have, indicating
// a bug downstream
if witness.is_empty() {
Ok(CommitmentTreeWitness {
Ok(MerklePath {
auth_path,
position,
})
@@ -504,16 +490,30 @@ impl<Node: Hashable> CommitmentTreeWitness<Node> {
Err(())
}
}
/// Returns the root of the tree corresponding to this path applied to `leaf`.
pub fn root(&self, leaf: Node) -> Node {
self.auth_path
.iter()
.enumerate()
.fold(
leaf,
|root, (i, (p, leaf_is_on_right))| match leaf_is_on_right {
false => Node::combine(i, &root, p),
true => Node::combine(i, p, &root),
},
)
}
}
#[cfg(test)]
mod tests {
use super::{CommitmentTree, CommitmentTreeWitness, Hashable, IncrementalWitness, PathFiller};
use sapling::Node;
use super::{CommitmentTree, Hashable, IncrementalWitness, MerklePath, PathFiller};
use crate::sapling::Node;
use ff::PrimeFieldRepr;
use hex;
use pairing::bls12_381::FrRepr;
use std::convert::TryInto;
use std::io::{self, Read, Write};
const HEX_EMPTY_ROOTS: [&str; 33] = [
@@ -607,7 +607,7 @@ mod tests {
self.0.root_inner(TESTING_DEPTH)
}
fn path(&self) -> Option<CommitmentTreeWitness<Node>> {
fn path(&self) -> Option<MerklePath<Node>> {
self.0.path_inner(TESTING_DEPTH)
}
}
@@ -1012,17 +1012,16 @@ mod tests {
assert_eq!(tree.size(), 0);
let mut witnesses = vec![];
let mut last_cm = None;
let mut paths_i = 0;
let mut witness_ser_i = 0;
for i in 0..16 {
let mut cm = FrRepr::default();
cm.read_le(&hex::decode(commitments[i]).unwrap()[..])
.expect("length is 32 bytes");
let cm = FrRepr(hex::decode(commitments[i]).unwrap()[..].try_into().unwrap());
let cm = Node::new(cm);
// Witness here
witnesses.push(TestIncrementalWitness::from_tree(&tree));
witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cm));
// Now append a commitment to the tree
assert!(tree.append(cm).is_ok());
@@ -1036,22 +1035,23 @@ mod tests {
// Check serialization of tree
assert_tree_ser_eq(&tree, tree_ser[i]);
let mut first = true; // The first witness can never form a path
for witness in witnesses.as_mut_slice() {
for (witness, leaf) in witnesses.as_mut_slice() {
// Append the same commitment to all the witnesses
assert!(witness.append(cm).is_ok());
if first {
assert!(witness.path().is_none());
} else {
if let Some(leaf) = leaf {
let path = witness.path().expect("should be able to create a path");
let expected = CommitmentTreeWitness::from_slice_with_depth(
let expected = MerklePath::from_slice_with_depth(
&mut hex::decode(paths[paths_i]).unwrap(),
TESTING_DEPTH,
)
.unwrap();
assert_eq!(path, expected);
assert_eq!(path.root(*leaf), witness.root());
paths_i += 1;
} else {
// The first witness can never form a path
assert!(witness.path().is_none());
}
// Check witness serialization
@@ -1059,15 +1059,15 @@ mod tests {
witness_ser_i += 1;
assert_eq!(witness.root(), tree.root());
first = false;
}
last_cm = Some(cm);
}
// Tree should be full now
let node = Node::blank();
assert!(tree.append(node).is_err());
for witness in witnesses.as_mut_slice() {
for (witness, _) in witnesses.as_mut_slice() {
assert!(witness.append(node).is_err());
}
}

View File

@@ -11,16 +11,17 @@ use crate::{
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf};
use ff::{PrimeField, PrimeFieldRepr};
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
use rand_core::{CryptoRng, RngCore};
use std::convert::TryInto;
use std::fmt;
use std::str;
use crate::{keys::OutgoingViewingKey, JUBJUB};
pub const KDF_SAPLING_PERSONALIZATION: &'static [u8; 16] = b"Zcash_SaplingKDF";
pub const PRF_OCK_PERSONALIZATION: &'static [u8; 16] = b"Zcash_Derive_ock";
pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF";
pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock";
const COMPACT_NOTE_SIZE: usize = (
1 + // version
@@ -85,7 +86,7 @@ impl Default for Memo {
impl PartialEq for Memo {
fn eq(&self, rhs: &Memo) -> bool {
&self.0[..] == &rhs.0[..]
self.0[..] == rhs.0[..]
}
}
@@ -106,11 +107,6 @@ impl Memo {
}
}
/// Returns a `Memo` containing the given string, or `None` if the string is too long.
pub fn from_str(memo: &str) -> Option<Memo> {
Memo::from_bytes(memo.as_bytes())
}
/// Returns the underlying bytes of the `Memo`.
pub fn as_bytes(&self) -> &[u8] {
&self.0[..]
@@ -134,6 +130,15 @@ impl Memo {
}
}
impl str::FromStr for Memo {
type Err = ();
/// Returns a `Memo` containing the given string, or an error if the string is too long.
fn from_str(memo: &str) -> Result<Self, Self::Err> {
Memo::from_bytes(memo.as_bytes()).ok_or(())
}
}
pub fn generate_esk<R: RngCore + CryptoRng>(rng: &mut R) -> Fs {
// create random 64 byte buffer
let mut buffer = [0u8; 64];
@@ -188,7 +193,7 @@ fn prf_ock(
let mut ock_input = [0u8; 128];
ock_input[0..32].copy_from_slice(&ovk.0);
cv.write(&mut ock_input[32..64]).unwrap();
cmu.into_repr().write_le(&mut ock_input[64..96]).unwrap();
ock_input[64..96].copy_from_slice(cmu.to_repr().as_ref());
epk.write(&mut ock_input[96..128]).unwrap();
Blake2bParams::new()
@@ -210,12 +215,12 @@ fn prf_ock(
/// ```
/// extern crate ff;
/// extern crate pairing;
/// extern crate rand_os;
/// extern crate rand_core;
/// extern crate zcash_primitives;
///
/// use ff::Field;
/// use pairing::bls12_381::Bls12;
/// use rand_os::OsRng;
/// use rand_core::OsRng;
/// use zcash_primitives::{
/// jubjub::fs::Fs,
/// keys::OutgoingViewingKey,
@@ -228,10 +233,7 @@ fn prf_ock(
///
/// let diversifier = Diversifier([0; 11]);
/// let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap();
/// let to = PaymentAddress {
/// pk_d,
/// diversifier,
/// };
/// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
/// let ovk = OutgoingViewingKey([0; 32]);
///
/// let value = 1000;
@@ -290,22 +292,18 @@ impl SaplingNoteEncryption {
/// Generates `encCiphertext` for this note.
pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] {
let shared_secret = sapling_ka_agree(&self.esk, &self.to.pk_d);
let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d());
let key = kdf_sapling(shared_secret, &self.epk);
// Note plaintext encoding is defined in section 5.5 of the Zcash Protocol
// Specification.
let mut input = [0; NOTE_PLAINTEXT_SIZE];
input[0] = 1;
input[1..12].copy_from_slice(&self.to.diversifier.0);
input[1..12].copy_from_slice(&self.to.diversifier().0);
(&mut input[12..20])
.write_u64::<LittleEndian>(self.note.value)
.unwrap();
self.note
.r
.into_repr()
.write_le(&mut input[20..COMPACT_NOTE_SIZE])
.unwrap();
input[20..COMPACT_NOTE_SIZE].copy_from_slice(self.note.r.to_repr().as_ref());
input[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE].copy_from_slice(&self.memo.0);
let mut output = [0u8; ENC_CIPHERTEXT_SIZE];
@@ -329,10 +327,7 @@ impl SaplingNoteEncryption {
let mut input = [0u8; OUT_PLAINTEXT_SIZE];
self.note.pk_d.write(&mut input[0..32]).unwrap();
self.esk
.into_repr()
.write_le(&mut input[32..OUT_PLAINTEXT_SIZE])
.unwrap();
input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(self.esk.to_repr().as_ref());
let mut output = [0u8; OUT_CIPHERTEXT_SIZE];
assert_eq!(
@@ -362,16 +357,18 @@ fn parse_note_plaintext_without_memo(
let v = (&plaintext[12..20]).read_u64::<LittleEndian>().ok()?;
let mut rcm = FsRepr::default();
rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?;
let rcm = Fs::from_repr(rcm).ok()?;
let rcm = Fs::from_repr(FsRepr(
plaintext[20..COMPACT_NOTE_SIZE]
.try_into()
.expect("slice is the correct length"),
))?;
let diversifier = Diversifier(d);
let pk_d = diversifier
.g_d::<Bls12>(&JUBJUB)?
.mul(ivk.into_repr(), &JUBJUB);
.mul(ivk.to_repr(), &JUBJUB);
let to = PaymentAddress { pk_d, diversifier };
let to = PaymentAddress::from_parts(diversifier, pk_d)?;
let note = to.create_note(v, rcm, &JUBJUB).unwrap();
if note.cm(&JUBJUB) != *cmu {
@@ -482,9 +479,11 @@ pub fn try_sapling_output_recovery(
.ok()?
.as_prime_order(&JUBJUB)?;
let mut esk = FsRepr::default();
esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).ok()?;
let esk = Fs::from_repr(esk).ok()?;
let esk = Fs::from_repr(FsRepr(
op[32..OUT_PLAINTEXT_SIZE]
.try_into()
.expect("slice is the correct length"),
))?;
let shared_secret = sapling_ka_agree(&esk, &pk_d);
let key = kdf_sapling(shared_secret, &epk);
@@ -514,9 +513,11 @@ pub fn try_sapling_output_recovery(
let v = (&plaintext[12..20]).read_u64::<LittleEndian>().ok()?;
let mut rcm = FsRepr::default();
rcm.read_le(&plaintext[20..COMPACT_NOTE_SIZE]).ok()?;
let rcm = Fs::from_repr(rcm).ok()?;
let rcm = Fs::from_repr(FsRepr(
plaintext[20..COMPACT_NOTE_SIZE]
.try_into()
.expect("slice is the correct length"),
))?;
let mut memo = [0u8; 512];
memo.copy_from_slice(&plaintext[COMPACT_NOTE_SIZE..NOTE_PLAINTEXT_SIZE]);
@@ -524,14 +525,14 @@ pub fn try_sapling_output_recovery(
let diversifier = Diversifier(d);
if diversifier
.g_d::<Bls12>(&JUBJUB)?
.mul(esk.into_repr(), &JUBJUB)
.mul(esk.to_repr(), &JUBJUB)
!= *epk
{
// Published epk doesn't match calculated epk
return None;
}
let to = PaymentAddress { pk_d, diversifier };
let to = PaymentAddress::from_parts(diversifier, pk_d)?;
let note = to.create_note(v, rcm, &JUBJUB).unwrap();
if note.cm(&JUBJUB) != *cmu {
@@ -553,10 +554,12 @@ mod tests {
primitives::{Diversifier, PaymentAddress, ValueCommitment},
};
use crypto_api_chachapoly::ChachaPolyIetf;
use ff::{Field, PrimeField, PrimeFieldRepr};
use ff::{Field, PrimeField};
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::OsRng;
use rand_core::{CryptoRng, RngCore};
use rand_os::OsRng;
use std::convert::TryInto;
use std::str::FromStr;
use super::{
kdf_sapling, prf_ock, sapling_ka_agree, try_sapling_compact_note_decryption,
@@ -661,16 +664,18 @@ mod tests {
0x74, 0x20, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68
])
);
assert!(Memo::from_str(
"thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \
meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \
but it's now a bit too long"
)
.is_none());
assert_eq!(
Memo::from_str(
"thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \
meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \
but it's now a bit too long"
),
Err(())
);
}
#[test]
@@ -691,10 +696,47 @@ mod tests {
[u8; ENC_CIPHERTEXT_SIZE],
[u8; OUT_CIPHERTEXT_SIZE],
) {
let diversifier = Diversifier([0; 11]);
let ivk = Fs::random(&mut rng);
let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) =
random_enc_ciphertext_with(ivk, rng);
assert!(try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext).is_some());
assert!(try_sapling_compact_note_decryption(
&ivk,
&epk,
&cmu,
&enc_ciphertext[..COMPACT_NOTE_SIZE]
)
.is_some());
assert!(try_sapling_output_recovery(
&ovk,
&cv,
&cmu,
&epk,
&enc_ciphertext,
&out_ciphertext
)
.is_some());
(ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext)
}
fn random_enc_ciphertext_with<R: RngCore + CryptoRng>(
ivk: Fs,
mut rng: &mut R,
) -> (
OutgoingViewingKey,
Fs,
edwards::Point<Bls12, Unknown>,
Fr,
edwards::Point<Bls12, PrimeOrder>,
[u8; ENC_CIPHERTEXT_SIZE],
[u8; OUT_CIPHERTEXT_SIZE],
) {
let diversifier = Diversifier([0; 11]);
let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap().mul(ivk, &JUBJUB);
let pa = PaymentAddress { diversifier, pk_d };
let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d);
// Construct the value commitment for the proof instance
let value = 100;
@@ -715,24 +757,6 @@ mod tests {
let enc_ciphertext = ne.encrypt_note_plaintext();
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);
assert!(try_sapling_note_decryption(&ivk, epk, &cmu, &enc_ciphertext).is_some());
assert!(try_sapling_compact_note_decryption(
&ivk,
epk,
&cmu,
&enc_ciphertext[..COMPACT_NOTE_SIZE]
)
.is_some());
assert!(try_sapling_output_recovery(
&ovk,
&cv,
&cmu,
&epk,
&enc_ciphertext,
&out_ciphertext
)
.is_some());
(
ovk,
ivk,
@@ -768,9 +792,7 @@ mod tests {
.as_prime_order(&JUBJUB)
.unwrap();
let mut esk = FsRepr::default();
esk.read_le(&op[32..OUT_PLAINTEXT_SIZE]).unwrap();
let esk = Fs::from_repr(esk).unwrap();
let esk = Fs::from_repr(FsRepr(op[32..OUT_PLAINTEXT_SIZE].try_into().unwrap())).unwrap();
let shared_secret = sapling_ka_agree(&esk, &pk_d);
let key = kdf_sapling(shared_secret, &epk);
@@ -1249,23 +1271,33 @@ mod tests {
);
}
#[test]
fn recovery_with_invalid_pk_d() {
let mut rng = OsRng;
let ivk = Fs::zero();
let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) =
random_enc_ciphertext_with(ivk, &mut rng);
assert_eq!(
try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext),
None
);
}
#[test]
fn test_vectors() {
let test_vectors = crate::test_vectors::note_encryption::make_test_vectors();
macro_rules! read_fr {
($field:expr) => {{
let mut repr = FrRepr::default();
repr.read_le(&$field[..]).unwrap();
Fr::from_repr(repr).unwrap()
Fr::from_repr(FrRepr($field[..].try_into().unwrap())).unwrap()
}};
}
macro_rules! read_fs {
($field:expr) => {{
let mut repr = FsRepr::default();
repr.read_le(&$field[..]).unwrap();
Fs::from_repr(repr).unwrap()
Fs::from_repr(FsRepr($field[..].try_into().unwrap())).unwrap()
}};
}
@@ -1310,10 +1342,7 @@ mod tests {
let ock = prf_ock(&ovk, &cv, &cmu, &epk);
assert_eq!(ock.as_bytes(), tv.ock);
let to = PaymentAddress {
pk_d,
diversifier: Diversifier(tv.default_d),
};
let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap();
let note = to.create_note(tv.v, rcm, &JUBJUB).unwrap();
assert_eq!(note.cm(&JUBJUB), cmu);

View File

@@ -1,5 +1,9 @@
use ff::{Field, PrimeField, PrimeFieldRepr};
use jubjub::*;
//! Implementation of the Pedersen hash function used in Sapling.
use crate::jubjub::*;
use byteorder::{ByteOrder, LittleEndian};
use ff::{Endianness, Field, PrimeField};
use std::ops::{AddAssign, Neg};
#[derive(Copy, Clone)]
pub enum Personalization {
@@ -55,14 +59,14 @@ where
if a {
tmp.add_assign(&cur);
}
cur.double(); // 2^1 * cur
cur = cur.double(); // 2^1 * cur
if b {
tmp.add_assign(&cur);
}
// conditionally negate
if c {
tmp.negate();
tmp = tmp.neg();
}
acc.add_assign(&tmp);
@@ -72,9 +76,7 @@ where
if chunks_remaining == 0 {
break;
} else {
cur.double(); // 2^2 * cur
cur.double(); // 2^3 * cur
cur.double(); // 2^4 * cur
cur = cur.double().double().double(); // 2^4 * cur
}
}
@@ -84,19 +86,32 @@ where
let mut table: &[Vec<edwards::Point<E, _>>] =
&generators.next().expect("we don't have enough generators");
let window = JubjubBls12::pedersen_hash_exp_window_size();
let window_mask = (1 << window) - 1;
let window = JubjubBls12::pedersen_hash_exp_window_size() as usize;
let window_mask = (1u64 << window) - 1;
let mut acc = acc.into_repr();
let mut acc = acc.to_repr();
<E::Fs as PrimeField>::ReprEndianness::toggle_little_endian(&mut acc);
let num_limbs: usize = acc.as_ref().len() / 8;
let mut limbs = vec![0u64; num_limbs + 1];
LittleEndian::read_u64_into(acc.as_ref(), &mut limbs[..num_limbs]);
let mut tmp = edwards::Point::zero();
while !acc.is_zero() {
let i = (acc.as_ref()[0] & window_mask) as usize;
let mut pos = 0;
while pos < E::Fs::NUM_BITS as usize {
let u64_idx = pos / 64;
let bit_idx = pos % 64;
let i = (if bit_idx + window < 64 {
// This window's bits are contained in a single u64.
limbs[u64_idx] >> bit_idx
} else {
// Combine the current u64's bits with the bits from the next u64.
(limbs[u64_idx] >> bit_idx) | (limbs[u64_idx + 1] << (64 - bit_idx))
} & window_mask) as usize;
tmp = tmp.add(&table[0][i], params);
acc.shr(window);
pos += window;
table = &table[1..];
}
@@ -105,3 +120,44 @@ where
result
}
#[cfg(test)]
pub mod test {
use super::*;
use crate::test_vectors::pedersen_hash_vectors;
use pairing::bls12_381::Bls12;
pub struct TestVector<'a> {
pub personalization: Personalization,
pub input_bits: Vec<u8>,
pub hash_x: &'a str,
pub hash_y: &'a str,
}
#[test]
fn test_pedersen_hash_points() {
let test_vectors = pedersen_hash_vectors::get_vectors();
assert!(test_vectors.len() > 0);
for v in test_vectors.iter() {
let params = &JubjubBls12::new();
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);
}
}
}

View File

@@ -1,14 +1,16 @@
use ff::{Field, PrimeField, PrimeFieldRepr};
//! Structs for core Zcash primitives.
use constants;
use ff::{Field, PrimeField};
use group_hash::group_hash;
use crate::constants;
use pedersen_hash::{pedersen_hash, Personalization};
use crate::group_hash::group_hash;
use crate::pedersen_hash::{pedersen_hash, Personalization};
use byteorder::{LittleEndian, WriteBytesExt};
use jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder};
use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder};
use blake2s_simd::Params as Blake2sParams;
@@ -22,7 +24,7 @@ impl<E: JubjubEngine> ValueCommitment<E> {
pub fn cm(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
params
.generator(FixedGenerators::ValueCommitmentValue)
.mul(self.value, params)
.mul(E::Fs::from(self.value), params)
.add(
&params
.generator(FixedGenerators::ValueCommitmentRandomness)
@@ -39,7 +41,7 @@ pub struct ProofGenerationKey<E: JubjubEngine> {
}
impl<E: JubjubEngine> ProofGenerationKey<E> {
pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
pub fn to_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
ViewingKey {
ak: self.ak.clone(),
nk: params
@@ -84,23 +86,20 @@ impl<E: JubjubEngine> ViewingKey<E> {
h[31] &= 0b0000_0111;
let mut e = <E::Fs as PrimeField>::Repr::default();
e.read_le(&h[..]).unwrap();
e.as_mut().copy_from_slice(&h[..]);
E::Fs::from_repr(e).expect("should be a valid scalar")
}
pub fn into_payment_address(
pub fn to_payment_address(
&self,
diversifier: Diversifier,
params: &E::Params,
) -> Option<PaymentAddress<E>> {
diversifier.g_d(params).map(|g_d| {
diversifier.g_d(params).and_then(|g_d| {
let pk_d = g_d.mul(self.ivk(), params);
PaymentAddress {
pk_d: pk_d,
diversifier: diversifier,
}
PaymentAddress::from_parts(diversifier, pk_d)
})
}
}
@@ -121,10 +120,16 @@ impl Diversifier {
}
}
/// A Sapling payment address.
///
/// # Invariants
///
/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
/// and not the identity).
#[derive(Clone, Debug)]
pub struct PaymentAddress<E: JubjubEngine> {
pub pk_d: edwards::Point<E, PrimeOrder>,
pub diversifier: Diversifier,
pk_d: edwards::Point<E, PrimeOrder>,
diversifier: Diversifier,
}
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
@@ -134,6 +139,67 @@ impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
}
impl<E: JubjubEngine> PaymentAddress<E> {
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Returns None if `pk_d` is the identity.
pub fn from_parts(
diversifier: Diversifier,
pk_d: edwards::Point<E, PrimeOrder>,
) -> Option<Self> {
if pk_d == edwards::Point::zero() {
None
} else {
Some(PaymentAddress { pk_d, diversifier })
}
}
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Only for test code, as this explicitly bypasses the invariant.
#[cfg(test)]
pub(crate) fn from_parts_unchecked(
diversifier: Diversifier,
pk_d: edwards::Point<E, PrimeOrder>,
) -> Self {
PaymentAddress { pk_d, diversifier }
}
/// Parses a PaymentAddress from bytes.
pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option<Self> {
let diversifier = {
let mut tmp = [0; 11];
tmp.copy_from_slice(&bytes[0..11]);
Diversifier(tmp)
};
// Check that the diversifier is valid
if diversifier.g_d::<E>(params).is_none() {
return None;
}
edwards::Point::<E, _>::read(&bytes[11..43], params)
.ok()?
.as_prime_order(params)
.and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
}
/// Returns the byte encoding of this `PaymentAddress`.
pub fn to_bytes(&self) -> [u8; 43] {
let mut bytes = [0; 43];
bytes[0..11].copy_from_slice(&self.diversifier.0);
self.pk_d.write(&mut bytes[11..]).unwrap();
bytes
}
/// Returns the [`Diversifier`] for this `PaymentAddress`.
pub fn diversifier(&self) -> &Diversifier {
&self.diversifier
}
/// Returns `pk_d` for this `PaymentAddress`.
pub fn pk_d(&self) -> &edwards::Point<E, PrimeOrder> {
&self.pk_d
}
pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> {
self.diversifier.g_d(params)
}
@@ -145,9 +211,9 @@ impl<E: JubjubEngine> PaymentAddress<E> {
params: &E::Params,
) -> Option<Note<E>> {
self.g_d(params).map(|g_d| Note {
value: value,
value,
r: randomness,
g_d: g_d,
g_d,
pk_d: self.pk_d.clone(),
})
}
@@ -225,7 +291,7 @@ impl<E: JubjubEngine> Note<E> {
let rho = self.cm_full_point(params).add(
&params
.generator(FixedGenerators::NullifierPosition)
.mul(position, params),
.mul(E::Fs::from(position), params),
params,
);
@@ -245,6 +311,6 @@ impl<E: JubjubEngine> Note<E> {
pub fn cm(&self, params: &E::Params) -> E::Fr {
// The commitment is in the prime order subgroup, so mapping the
// commitment to the x-coordinate is an injective encoding.
self.cm_full_point(params).into_xy().0
self.cm_full_point(params).to_xy().0
}
}

View File

@@ -7,7 +7,7 @@ use crate::{
use pairing::bls12_381::{Bls12, Fr};
use crate::{
merkle_tree::CommitmentTreeWitness,
merkle_tree::MerklePath,
redjubjub::{PublicKey, Signature},
sapling::Node,
transaction::components::{Amount, GROTH_PROOF_SIZE},
@@ -35,7 +35,7 @@ pub trait TxProver {
ar: Fs,
value: u64,
anchor: Fr,
witness: CommitmentTreeWitness<Node>,
merkle_path: MerklePath<Node>,
) -> Result<
(
[u8; GROTH_PROOF_SIZE],
@@ -74,7 +74,7 @@ pub trait TxProver {
pub(crate) mod mock {
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand_os::OsRng;
use rand_core::OsRng;
use crate::{
jubjub::{edwards, fs::Fs, FixedGenerators, Unknown},
@@ -82,7 +82,7 @@ pub(crate) mod mock {
};
use crate::{
merkle_tree::CommitmentTreeWitness,
merkle_tree::MerklePath,
redjubjub::{PublicKey, Signature},
sapling::Node,
transaction::components::{Amount, GROTH_PROOF_SIZE},
@@ -108,7 +108,7 @@ pub(crate) mod mock {
ar: Fs,
value: u64,
_anchor: Fr,
_witness: CommitmentTreeWitness<Node>,
_merkle_path: MerklePath<Node>,
) -> Result<
(
[u8; GROTH_PROOF_SIZE],

View File

@@ -1,28 +1,26 @@
//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve.
//! See section 5.4.6 of the Sapling protocol specification.
//! Implementation of [RedJubjub], a specialization of RedDSA to the Jubjub
//! curve.
//!
//! [RedJubjub]: https://zips.z.cash/protocol/protocol.pdf#concretereddsa
use crate::jubjub::{edwards::Point, FixedGenerators, JubjubEngine, JubjubParams, Unknown};
use ff::{Field, PrimeField, PrimeFieldRepr};
use ff::{Field, PrimeField};
use rand_core::RngCore;
use std::io::{self, Read, Write};
use std::ops::{AddAssign, MulAssign, Neg};
use util::hash_to_scalar;
use crate::util::hash_to_scalar;
fn read_scalar<E: JubjubEngine, R: Read>(reader: R) -> io::Result<E::Fs> {
fn read_scalar<E: JubjubEngine, R: Read>(mut reader: R) -> io::Result<E::Fs> {
let mut s_repr = <E::Fs as PrimeField>::Repr::default();
s_repr.read_le(reader)?;
reader.read_exact(s_repr.as_mut())?;
match E::Fs::from_repr(s_repr) {
Ok(s) => Ok(s),
Err(_) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"scalar is not in field",
)),
}
E::Fs::from_repr(s_repr)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "scalar is not in field"))
}
fn write_scalar<E: JubjubEngine, W: Write>(s: &E::Fs, writer: W) -> io::Result<()> {
s.into_repr().write_le(writer)
fn write_scalar<E: JubjubEngine, W: Write>(s: &E::Fs, mut writer: W) -> io::Result<()> {
writer.write_all(s.to_repr().as_ref())
}
fn h_star<E: JubjubEngine>(a: &[u8], b: &[u8]) -> E::Fs {
@@ -191,7 +189,7 @@ pub fn batch_verify<'a, E: JubjubEngine, R: RngCore>(
let z = E::Fs::random(rng);
s.mul_assign(&z);
s.negate();
s = s.neg();
r = r.mul(z, params);

View File

@@ -5,14 +5,15 @@ use crate::{
pedersen_hash::{pedersen_hash, Personalization},
primitives::Note,
};
use ff::{BitIterator, PrimeField, PrimeFieldRepr};
use ff::{BitIterator, PrimeField};
use lazy_static::lazy_static;
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use rand_core::{CryptoRng, RngCore};
use std::io::{self, Read, Write};
use crate::merkle_tree::Hashable;
use crate::redjubjub::{PrivateKey, PublicKey, Signature};
use JUBJUB;
use crate::JUBJUB;
pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
@@ -20,7 +21,7 @@ pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
let lhs = {
let mut tmp = [false; 256];
for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(lhs)) {
for (a, b) in tmp.iter_mut().rev().zip(BitIterator::<u8, _>::new(lhs)) {
*a = b;
}
tmp
@@ -28,7 +29,7 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
let rhs = {
let mut tmp = [false; 256];
for (a, b) in tmp.iter_mut().rev().zip(BitIterator::new(rhs)) {
for (a, b) in tmp.iter_mut().rev().zip(BitIterator::<u8, _>::new(rhs)) {
*a = b;
}
tmp
@@ -37,14 +38,14 @@ pub fn merkle_hash(depth: usize, lhs: &FrRepr, rhs: &FrRepr) -> FrRepr {
pedersen_hash::<Bls12, _>(
Personalization::MerkleTree(depth),
lhs.iter()
.map(|&x| x)
.copied()
.take(Fr::NUM_BITS as usize)
.chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)),
.chain(rhs.iter().copied().take(Fr::NUM_BITS as usize)),
&JUBJUB,
)
.into_xy()
.to_xy()
.0
.into_repr()
.to_repr()
}
/// A node within the Sapling commitment tree.
@@ -61,13 +62,13 @@ impl Node {
impl Hashable for Node {
fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut repr = FrRepr::default();
repr.read_le(&mut reader)?;
let mut repr = FrRepr([0; 32]);
reader.read_exact(&mut repr.0)?;
Ok(Node::new(repr))
}
fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.repr.write_le(&mut writer)
writer.write_all(self.repr.as_ref())
}
fn combine(depth: usize, lhs: &Self, rhs: &Self) -> Self {
@@ -78,7 +79,7 @@ impl Hashable for Node {
fn blank() -> Self {
Node {
repr: Note::<Bls12>::uncommitted().into_repr(),
repr: Note::<Bls12>::uncommitted().to_repr(),
}
}

View File

@@ -70,7 +70,7 @@ impl Vector {
F: Fn(&mut R) -> io::Result<E>,
{
let count = CompactSize::read(&mut reader)?;
(0..count).into_iter().map(|_| func(&mut reader)).collect()
(0..count).map(|_| func(&mut reader)).collect()
}
pub fn write<W: Write, E, F>(mut writer: W, vec: &[E], func: F) -> io::Result<()>

View File

@@ -1 +1,2 @@
pub(crate) mod note_encryption;
pub(crate) mod pedersen_hash_vectors;

View File

@@ -0,0 +1,715 @@
//! Test vectors from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_pedersen.py
use crate::pedersen_hash::{test::TestVector, Personalization};
pub fn get_vectors<'a>() -> Vec<TestVector<'a>> {
return 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, 1, 0, 0],
hash_x: "Fr(0x4f8ce0e0a9e674b3ab9606a7d7aefba386e81583d81918127814cde41d209d97)",
hash_y: "Fr(0x312b5ab93b14c9b9af334fe1fe3c50fffb53fbd074fa40ca600febde7c97e346)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1,
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1,
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1,
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
],
hash_x: "Fr(0x599ab788360ae8c6d5bb7618aec37056d6227408d857fdc394078a3d7afdfe0f)",
hash_y: "Fr(0x4320c373da670e28d168f4ffd72b43208e8c815f40841682c57a3ee1d005a527)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
],
hash_x: "Fr(0x2da510317620f5dfdce1f31db6019f947eedcf02ff2972cff597a5c3ad21f5dd)",
hash_y: "Fr(0x198789969c0c33e6c359b9da4a51771f4d50863f36beef90436944fe568399f2)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
],
hash_x: "Fr(0x601247c7e640992d193dfb51df6ed93446687a7f2bcd0e4a598e6feb1ef20c40)",
hash_y: "Fr(0x371931733b73e7b95c2cad55a6cebd15c83619f697c64283e54e5ef61442a743)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1,
1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1,
0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1,
1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0,
1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0,
0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,
0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1,
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0,
1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1,
0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1,
1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
],
hash_x: "Fr(0x314192ecb1f2d8806a8108704c875a25d9fb7e444f9f373919adedebe8f2ae27)",
hash_y: "Fr(0x6b12b32f1372ad574799dee9eb591d961b704bf611f55fcc71f7e82cd3330b74)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1,
0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0,
1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1,
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0,
1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1,
1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0,
1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,
0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1,
0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
0,
],
hash_x: "Fr(0x0666c2bce7f362a2b807d212e9a577f116891a932affd7addec39fbf372c494e)",
hash_y: "Fr(0x6758bccfaf2e47c07756b96edea23aa8d10c33b38220bd1c411af612eeec18ab)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0,
0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1,
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0,
1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0,
1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1,
1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1,
0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0,
0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1,
1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0,
1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
],
hash_x: "Fr(0x130afe02b99375484efb0998f5331d2178e1d00e803049bb0769099420624f5f)",
hash_y: "Fr(0x5e2fc6970554ffe358652aa7968ac4fcf3de0c830e6ea492e01a38fafb68cd71)",
},
TestVector {
personalization: Personalization::NoteCommitment,
input_bits: vec![
1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1,
0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0,
0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1,
0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1,
1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1,
1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1,
0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1,
0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1,
1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1,
1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1,
1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0,
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1,
],
hash_x: "Fr(0x67914ebd539961b70f468fa23d4cb42133693a8ac57cd35a1e6369fe34fbedf7)",
hash_y: "Fr(0x44770870c0f0cfe59a10df95d6c21e6f1514a2f464b66377599438c126052d9f)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0],
hash_x: "Fr(0x62454a957289b3930d10f3def0d512cfe0ef3de06421321221af3558de9d481d)",
hash_y: "Fr(0x0279f0aebfb66e53ff69fba16b6608dbf4319b944432f45c6e69a3dbd1f7b330)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0, 0],
hash_x: "Fr(0x283c7880f35179e201161402d9c4556b255917dbbf0142ae60519787d36d4dea)",
hash_y: "Fr(0x648224408b4b83297cd0feb4cdc4eeb224237734931145432793bcd414228dc4)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0, 1],
hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)",
hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![0, 0, 0, 0, 0, 0, 1, 0, 0],
hash_x: "Fr(0x1f1086b287636a20063c9614db2de66bb7d49242e88060956a5e5845057f6f5d)",
hash_y: "Fr(0x6b1b395421dde74d53341caa9e01f39d7a3138efb9b57fc0381f98f4868df622)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0,
0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1,
0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0,
1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1,
1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
],
hash_x: "Fr(0x20d2b1b0551efe511755d564f8da4f5bf285fd6051331fa5f129ad95b318f6cd)",
hash_y: "Fr(0x2834d96950de67ae80e85545f8333c6e14b5cf5be7325dac768f401e6edd9544)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0,
0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0,
],
hash_x: "Fr(0x01f4850a0f40e07186fee1f0a276f52fb12cffe05c18eb2aa18170330a93c555)",
hash_y: "Fr(0x19b0807358e7c8cba9168815ec54c4cd76997c34c592607d172151c48d5377cb)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1,
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1,
0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1,
1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0,
],
hash_x: "Fr(0x26dd81a3ffa37452c6a932d41eb4f2e0fedd531e9af8c2a7935b91dff653879d)",
hash_y: "Fr(0x2fc7aebb729ef5cabf0fb3f883bc2eb2603093850b0ec19c1a3c08b653e7f27f)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1,
0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0,
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1,
1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1,
1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0,
0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1,
0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0,
0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1,
0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0,
0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0,
1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1,
0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1,
0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1,
],
hash_x: "Fr(0x1111740552773b00aa6a2334575aa94102cfbd084290a430c90eb56d6db65b85)",
hash_y: "Fr(0x6560c44b11683c20030626f89456f78a53ae8a89f565956a98ffc554b48fbb1a)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0,
0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0,
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0,
0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1,
1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0,
1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1,
0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1,
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1,
1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0,
1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1,
1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0,
1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0,
1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0,
1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1,
1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
0,
],
hash_x: "Fr(0x429349ea9b5f8163bcda3014b3e15554df5173353fd73f315a49360c97265f68)",
hash_y: "Fr(0x188774bb6de41eba669be5d368942783f937acf2f418385fc5c78479b0a405ee)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0,
0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1,
1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1,
1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1,
1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0,
1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0,
0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1,
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,
0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1,
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0,
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1,
0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0,
0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0,
],
hash_x: "Fr(0x00e827f3ed136f3c91c61c97ab9b7cca0ea53c20e47abb5e226ede297bdd5f37)",
hash_y: "Fr(0x315cc00a54972df6a19f650d3fab5f2ad0fb07397bacb6944568618f2aa76bf6)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1,
1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0,
0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1,
1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0,
0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1,
0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0,
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0,
1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0,
1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1,
0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0,
0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1,
1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1,
0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
],
hash_x: "Fr(0x3ee50557c4aa9158c4bb9d5961208e6c62f55c73ad7c7695a0eba0bcb6d83d05)",
hash_y: "Fr(0x1b1a2be6e47688828aeadf2d37db298eac0c2736c2722b227871fdeeee29de33)",
},
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, 1, 0, 0],
hash_x: "Fr(0x52112dd7a4293d049bb011683244a0f957e6ba95e1d1cf2fb6654d449a6d3fbc)",
hash_y: "Fr(0x2ae14ecd81bb5b4489d2d64b5d2eb92a684087b28dd9a4950ecdb78c014e178c)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1,
1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0,
1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0,
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
],
hash_x: "Fr(0x544a0b44c35dca64ee806d1af70b7c44134e5d86efed413947657ffd71adf9b2)",
hash_y: "Fr(0x5ddc5dbf12abbbc5561defd3782a32f450b3c398f52ff4629677e59e86e3ab31)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0,
0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
],
hash_x: "Fr(0x6cb6490ccb0ca9ccd657146f58a7b800bc4fb2556ee37861227ee8fda724acfb)",
hash_y: "Fr(0x05c6fe100926f5cc441e54e72f024b6b12c907f2ec5680335057896411984c9f)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1,
0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1,
0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1,
0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0,
],
hash_x: "Fr(0x40901e2175cb7f06a00c676d54d90e59fd448f11cbbc5eb517f9fea74b795ce2)",
hash_y: "Fr(0x42d512891f91087310c9bc630c8d0ecc014596f884fd6df55dada8195ed726de)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1,
1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1,
1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1,
1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0,
1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1,
0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1,
1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0,
1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0,
1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0,
1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1,
0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
],
hash_x: "Fr(0x66a433542419f1a086ed0663b0e8df2ece9a04065f147896976baba1a916b6dc)",
hash_y: "Fr(0x203bd3672522e1d3c86fa6b9f3b58f20199a4216adfd40982add13a856f6f3de)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1,
1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1,
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1,
1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1,
1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,
0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0,
1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1,
1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0,
1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1,
0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0,
0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1,
1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0,
1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0,
1,
],
hash_x: "Fr(0x119db3b38086c1a3c6c6f53c529ee62d9311d69c2d8aeeafa6e172e650d3afda)",
hash_y: "Fr(0x72287540be7d2b0f58f5c73eaa53c55bea6b79dd79873b4e47cc11787bb9a15d)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1,
1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0,
0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0,
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0,
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0,
1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0,
1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1,
1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1,
0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0,
1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,
0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1,
1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0,
0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,
1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0,
1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0,
0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0,
0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0,
],
hash_x: "Fr(0x446efdcf89b70ba2b03427a0893008181d0fc4e76b84b1a500d7ee523c8e3666)",
hash_y: "Fr(0x125ee0048efb0372b92c3c15d51a7c5c77a712054cc4fdd0774563da46ec7289)",
},
TestVector {
personalization: Personalization::MerkleTree(34),
input_bits: vec![
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1,
0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0,
1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1,
0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0,
0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0,
1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0,
0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1,
1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1,
0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0,
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,
0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1,
1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1,
1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1,
1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0,
0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0,
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1,
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1,
1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,
0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0,
0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1,
1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
],
hash_x: "Fr(0x72723bf0573bcb4b72d4184cfeb707d9556b7f705f56a4652707a36f2edf10f7)",
hash_y: "Fr(0x3a7f0999a6a1393bd49fc82302e7352e01176fbebb0192bf5e6ef39eb8c585ad)",
},
TestVector {
personalization: Personalization::MerkleTree(27),
input_bits: vec![
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
],
hash_x: "Fr(0x414f6ba05f6b92da1f9051950769e1083d05615def32b016ae424309828a11f4)",
hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)",
},
TestVector {
personalization: Personalization::MerkleTree(36),
input_bits: vec![
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
],
hash_x: "Fr(0x62d6fe1e373225a5695f3115aed8265c59e2d6275ceef6bbc53fde3fc6594024)",
hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)",
},
TestVector {
personalization: Personalization::MerkleTree(0),
input_bits: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
hash_x: "Fr(0x1116a934f26b57a2c9daa6f25ac9b1a8f9dacddba30f65433ac021bf39a6bfdd)",
hash_y: "Fr(0x407275be7d5a4c48204c8d83f5b211d09a2f285d4f0f87a928d4de9a6338e1d1)",
},
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, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
],
hash_x: "Fr(0x329e3bb2ca31ea6e13a986730237f6fd16b842a510cbabe851bdbcf57d75ee0d)",
hash_y: "Fr(0x471d2109656afcb96d0609b371b132b97efcf72c6051064dd19fdc004799bfa9)",
},
];
}

View File

@@ -1,5 +1,6 @@
//! Structs for building transactions.
use crate::zip32::ExtendedSpendingKey;
use crate::{
jubjub::fs::Fs,
primitives::{Diversifier, Note, PaymentAddress},
@@ -7,14 +8,12 @@ use crate::{
use ff::Field;
use pairing::bls12_381::{Bls12, Fr};
use rand::{rngs::OsRng, seq::SliceRandom, CryptoRng, RngCore};
use std::error;
use std::fmt;
use zip32::ExtendedSpendingKey;
use crate::{
consensus,
keys::OutgoingViewingKey,
legacy::TransparentAddress,
merkle_tree::{CommitmentTreeWitness, IncrementalWitness},
merkle_tree::MerklePath,
note_encryption::{generate_esk, Memo, SaplingNoteEncryption},
prover::TxProver,
redjubjub::PrivateKey,
@@ -45,38 +44,16 @@ pub enum Error {
ChangeIsNegative(Amount),
InvalidAddress,
InvalidAmount,
InvalidWitness,
NoChangeAddress,
SpendProof,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::AnchorMismatch => {
write!(f, "Anchor mismatch (anchors for all spends must be equal)")
}
Error::BindingSig => write!(f, "Failed to create bindingSig"),
Error::ChangeIsNegative(amount) => {
write!(f, "Change is negative ({:?} zatoshis)", amount)
}
Error::InvalidAddress => write!(f, "Invalid address"),
Error::InvalidAmount => write!(f, "Invalid amount"),
Error::InvalidWitness => write!(f, "Invalid note witness"),
Error::NoChangeAddress => write!(f, "No change address specified or discoverable"),
Error::SpendProof => write!(f, "Failed to create Sapling spend proof"),
}
}
}
impl error::Error for Error {}
struct SpendDescriptionInfo {
extsk: ExtendedSpendingKey,
diversifier: Diversifier,
note: Note<Bls12>,
alpha: Fs,
witness: CommitmentTreeWitness<Node>,
merkle_path: MerklePath<Node>,
}
pub struct SaplingOutput {
@@ -106,7 +83,7 @@ impl SaplingOutput {
let note = Note {
g_d,
pk_d: to.pk_d.clone(),
pk_d: to.pk_d().clone(),
value: value.into(),
r: rcm,
};
@@ -187,7 +164,38 @@ impl Default for TransparentInputs {
struct TransparentInputs;
impl TransparentInputs {
fn input_sum(&self) -> Amount {
#[cfg(feature = "transparent-inputs")]
fn push(
&mut self,
mtx: &mut TransactionData,
sk: secp256k1::SecretKey,
utxo: OutPoint,
coin: TxOut,
) -> Result<(), Error> {
if coin.value.is_negative() {
return Err(Error::InvalidAmount);
}
let pubkey = secp256k1::PublicKey::from_secret_key(&self.secp, &sk).serialize();
match coin.script_pubkey.address() {
Some(TransparentAddress::PublicKey(hash)) => {
use ripemd160::Ripemd160;
use sha2::{Digest, Sha256};
if &hash[..] != &Ripemd160::digest(&Sha256::digest(&pubkey))[..] {
return Err(Error::InvalidAddress);
}
}
_ => return Err(Error::InvalidAddress),
}
mtx.vin.push(TxIn::new(utxo));
self.inputs.push(TransparentInputInfo { sk, pubkey, coin });
Ok(())
}
fn value_sum(&self) -> Amount {
#[cfg(feature = "transparent-inputs")]
{
self.inputs
@@ -201,6 +209,36 @@ impl TransparentInputs {
Amount::zero()
}
}
#[cfg(feature = "transparent-inputs")]
fn apply_signatures(
&self,
mtx: &mut TransactionData,
consensus_branch_id: consensus::BranchId,
) {
let mut sighash = [0u8; 32];
for (i, info) in self.inputs.iter().enumerate() {
sighash.copy_from_slice(&signature_hash_data(
mtx,
consensus_branch_id,
SIGHASH_ALL,
Some((i, &info.coin.script_pubkey, info.coin.value)),
));
let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes");
let sig = self.secp.sign(&msg, &info.sk);
// Signature has to have "SIGHASH_ALL" appended to it
let mut sig_bytes: Vec<u8> = sig.serialize_der()[..].to_vec();
sig_bytes.extend(&[SIGHASH_ALL as u8]);
// P2PKH scriptSig
mtx.vin[i].script_sig = Script::default() << &sig_bytes[..] << &info.pubkey[..];
}
}
#[cfg(not(feature = "transparent-inputs"))]
fn apply_signatures(&self, _: &mut TransactionData, _: consensus::BranchId) {}
}
/// Metadata about a transaction created by a [`Builder`].
@@ -226,7 +264,7 @@ impl TransactionMetadata {
/// they added (via the first call to [`Builder::add_sapling_spend`]) is the first
/// [`SpendDescription`] in the transaction.
pub fn spend_index(&self, n: usize) -> Option<usize> {
self.spend_indices.get(n).map(|i| *i)
self.spend_indices.get(n).copied()
}
/// Returns the index within the transaction of the [`OutputDescription`] corresponding
@@ -237,7 +275,7 @@ impl TransactionMetadata {
/// they added (via the first call to [`Builder::add_sapling_output`]) is the first
/// [`OutputDescription`] in the transaction.
pub fn output_index(&self, n: usize) -> Option<usize> {
self.output_indices.get(n).map(|i| *i)
self.output_indices.get(n).copied()
}
}
@@ -249,7 +287,7 @@ pub struct Builder<R: RngCore + CryptoRng> {
anchor: Option<Fr>,
spends: Vec<SpendDescriptionInfo>,
outputs: Vec<SaplingOutput>,
legacy: TransparentInputs,
transparent_inputs: TransparentInputs,
change_address: Option<(OutgoingViewingKey, PaymentAddress<Bls12>)>,
}
@@ -289,32 +327,32 @@ impl<R: RngCore + CryptoRng> Builder<R> {
anchor: None,
spends: vec![],
outputs: vec![],
legacy: TransparentInputs::default(),
transparent_inputs: TransparentInputs::default(),
change_address: None,
}
}
/// Adds a Sapling note to be spent in this transaction.
///
/// Returns an error if the given witness does not have the same anchor as previous
/// witnesses, or has no path.
/// Returns an error if the given Merkle path does not have the same anchor as the
/// paths for previous Sapling notes.
pub fn add_sapling_spend(
&mut self,
extsk: ExtendedSpendingKey,
diversifier: Diversifier,
note: Note<Bls12>,
witness: IncrementalWitness<Node>,
merkle_path: MerklePath<Node>,
) -> Result<(), Error> {
// Consistency check: all anchors must equal the first one
let cm = Node::new(note.cm(&JUBJUB).into());
if let Some(anchor) = self.anchor {
let witness_root: Fr = witness.root().into();
if witness_root != anchor {
let path_root: Fr = merkle_path.root(cm).into();
if path_root != anchor {
return Err(Error::AnchorMismatch);
}
} else {
self.anchor = Some(witness.root().into())
self.anchor = Some(merkle_path.root(cm).into())
}
let witness = witness.path().ok_or(Error::InvalidWitness)?;
let alpha = Fs::random(&mut self.rng);
@@ -325,7 +363,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
diversifier,
note,
alpha,
witness,
merkle_path,
});
Ok(())
@@ -356,29 +394,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
utxo: OutPoint,
coin: TxOut,
) -> Result<(), Error> {
if coin.value.is_negative() {
return Err(Error::InvalidAmount);
}
let pubkey = secp256k1::PublicKey::from_secret_key(&self.legacy.secp, &sk).serialize();
match coin.script_pubkey.address() {
Some(TransparentAddress::PublicKey(hash)) => {
use ripemd160::Ripemd160;
use sha2::{Digest, Sha256};
if &hash[..] != &Ripemd160::digest(&Sha256::digest(&pubkey))[..] {
return Err(Error::InvalidAddress);
}
}
_ => return Err(Error::InvalidAddress),
}
self.mtx.vin.push(TxIn::new(utxo));
self.legacy
.inputs
.push(TransparentInputInfo { sk, pubkey, coin });
Ok(())
self.transparent_inputs.push(&mut self.mtx, sk, utxo, coin)
}
/// Adds a transparent address to send funds to.
@@ -418,8 +434,8 @@ impl<R: RngCore + CryptoRng> Builder<R> {
/// the network.
pub fn build(
mut self,
consensus_branch_id: u32,
prover: impl TxProver,
consensus_branch_id: consensus::BranchId,
prover: &impl TxProver,
) -> Result<(Transaction, TransactionMetadata), Error> {
let mut tx_metadata = TransactionMetadata::new();
@@ -428,7 +444,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
//
// Valid change
let change = self.mtx.value_balance - self.fee + self.legacy.input_sum()
let change = self.mtx.value_balance - self.fee + self.transparent_inputs.value_sum()
- self
.mtx
.vout
@@ -451,10 +467,11 @@ impl<R: RngCore + CryptoRng> Builder<R> {
} else if !self.spends.is_empty() {
(
self.spends[0].extsk.expsk.ovk,
PaymentAddress {
diversifier: self.spends[0].diversifier,
pk_d: self.spends[0].note.pk_d.clone(),
},
PaymentAddress::from_parts(
self.spends[0].diversifier,
self.spends[0].note.pk_d.clone(),
)
.ok_or(Error::InvalidAddress)?,
)
} else {
return Err(Error::NoChangeAddress);
@@ -498,16 +515,16 @@ impl<R: RngCore + CryptoRng> Builder<R> {
let binding_sig_needed = !spends.is_empty() || !outputs.is_empty();
// Create Sapling SpendDescriptions
if spends.len() > 0 {
if !spends.is_empty() {
let anchor = self.anchor.expect("anchor was set if spends were added");
for (i, (pos, spend)) in spends.iter().enumerate() {
let proof_generation_key = spend.extsk.expsk.proof_generation_key(&JUBJUB);
let mut nullifier = [0u8; 32];
nullifier.copy_from_slice(&spend.note.nf(
&proof_generation_key.into_viewing_key(&JUBJUB),
spend.witness.position,
&proof_generation_key.to_viewing_key(&JUBJUB),
spend.merkle_path.position,
&JUBJUB,
));
@@ -520,13 +537,13 @@ impl<R: RngCore + CryptoRng> Builder<R> {
spend.alpha,
spend.note.value,
anchor,
spend.witness.clone(),
spend.merkle_path.clone(),
)
.map_err(|()| Error::SpendProof)?;
self.mtx.shielded_spends.push(SpendDescription {
cv,
anchor: anchor,
anchor,
nullifier,
rk,
zkproof,
@@ -544,7 +561,7 @@ impl<R: RngCore + CryptoRng> Builder<R> {
// Record the post-randomized output location
tx_metadata.output_indices[pos] = i;
output.build(&prover, &mut ctx, &mut self.rng)
output.build(prover, &mut ctx, &mut self.rng)
} else {
// This is a dummy output
let (dummy_to, dummy_note) = {
@@ -563,16 +580,16 @@ impl<R: RngCore + CryptoRng> Builder<R> {
(diversifier, g_d)
};
let pk_d = {
let (pk_d, payment_address) = loop {
let dummy_ivk = Fs::random(&mut self.rng);
g_d.mul(dummy_ivk, &JUBJUB)
let pk_d = g_d.mul(dummy_ivk, &JUBJUB);
if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d.clone()) {
break (pk_d, addr);
}
};
(
PaymentAddress {
diversifier,
pk_d: pk_d.clone(),
},
payment_address,
Note {
g_d,
pk_d,
@@ -643,28 +660,8 @@ impl<R: RngCore + CryptoRng> Builder<R> {
}
// Transparent signatures
#[cfg(feature = "transparent-inputs")]
{
for (i, info) in self.legacy.inputs.iter().enumerate() {
sighash.copy_from_slice(&signature_hash_data(
&self.mtx,
consensus_branch_id,
SIGHASH_ALL,
Some((i, &info.coin.script_pubkey, info.coin.value)),
));
let msg = secp256k1::Message::from_slice(&sighash).expect("32 bytes");
let sig = self.legacy.secp.sign(&msg, &info.sk);
// Signature has to have "SIGHASH_ALL" appended to it
let mut sig_bytes: Vec<u8> = sig.serialize_der()[..].to_vec();
sig_bytes.extend(&[SIGHASH_ALL as u8]);
// P2PKH scriptSig
self.mtx.vin[i].script_sig =
Script::default() << &sig_bytes[..] << &info.pubkey[..];
}
}
self.transparent_inputs
.apply_signatures(&mut self.mtx, consensus_branch_id);
Ok((
self.mtx.freeze().expect("Transaction should be complete"),
@@ -676,12 +673,13 @@ impl<R: RngCore + CryptoRng> Builder<R> {
#[cfg(test)]
mod tests {
use ff::{Field, PrimeField};
use rand::rngs::OsRng;
use rand_core::OsRng;
use crate::jubjub::fs::Fs;
use super::{Builder, Error};
use crate::{
consensus,
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
prover::mock::MockTxProver,
@@ -737,6 +735,77 @@ mod tests {
assert!(tx.binding_sig.is_none());
}
#[test]
fn binding_sig_absent_if_no_shielded_spend_or_output() {
use crate::transaction::{
builder::{self, TransparentInputs},
TransactionData,
};
// Create a builder with 0 fee, so we can construct t outputs
let mut builder = builder::Builder {
rng: OsRng,
mtx: TransactionData::new(),
fee: Amount::zero(),
anchor: None,
spends: vec![],
outputs: vec![],
transparent_inputs: TransparentInputs::default(),
change_address: None,
};
// Create a tx with only t output. No binding_sig should be present
builder
.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero())
.unwrap();
let (tx, _) = builder
.build(consensus::BranchId::Sapling, &MockTxProver)
.unwrap();
// No binding signature, because only t input and outputs
assert!(tx.binding_sig.is_none());
}
#[test]
fn binding_sig_present_if_shielded_spend() {
let extsk = ExtendedSpendingKey::master(&[]);
let extfvk = ExtendedFullViewingKey::from(&extsk);
let to = extfvk.default_address().unwrap().1;
let mut rng = OsRng;
let note1 = to
.create_note(50000, Fs::random(&mut rng), &JUBJUB)
.unwrap();
let cm1 = Node::new(note1.cm(&JUBJUB).to_repr());
let mut tree = CommitmentTree::new();
tree.append(cm1).unwrap();
let witness1 = IncrementalWitness::from_tree(&tree);
let mut builder = Builder::new(0);
// Create a tx with a sapling spend. binding_sig should be present
builder
.add_sapling_spend(
extsk.clone(),
*to.diversifier(),
note1.clone(),
witness1.path().unwrap(),
)
.unwrap();
builder
.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero())
.unwrap();
// Expect a binding signature error, because our inputs aren't valid, but this shows
// that a binding signature was attempted
assert_eq!(
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::BindingSig)
);
}
#[test]
fn fails_on_negative_transparent_output() {
let mut builder = Builder::new(0);
@@ -761,7 +830,7 @@ mod tests {
{
let builder = Builder::new(0);
assert_eq!(
builder.build(1, MockTxProver),
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::ChangeIsNegative(Amount::from_i64(-10000).unwrap()))
);
}
@@ -783,7 +852,7 @@ mod tests {
)
.unwrap();
assert_eq!(
builder.build(1, MockTxProver),
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::ChangeIsNegative(Amount::from_i64(-60000).unwrap()))
);
}
@@ -799,7 +868,7 @@ mod tests {
)
.unwrap();
assert_eq!(
builder.build(1, MockTxProver),
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::ChangeIsNegative(Amount::from_i64(-60000).unwrap()))
);
}
@@ -807,7 +876,7 @@ mod tests {
let note1 = to
.create_note(59999, Fs::random(&mut rng), &JUBJUB)
.unwrap();
let cm1 = Node::new(note1.cm(&JUBJUB).into_repr());
let cm1 = Node::new(note1.cm(&JUBJUB).to_repr());
let mut tree = CommitmentTree::new();
tree.append(cm1).unwrap();
let mut witness1 = IncrementalWitness::from_tree(&tree);
@@ -819,9 +888,9 @@ mod tests {
builder
.add_sapling_spend(
extsk.clone(),
to.diversifier,
*to.diversifier(),
note1.clone(),
witness1.clone(),
witness1.path().unwrap(),
)
.unwrap();
builder
@@ -839,13 +908,13 @@ mod tests {
)
.unwrap();
assert_eq!(
builder.build(1, MockTxProver),
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::ChangeIsNegative(Amount::from_i64(-1).unwrap()))
);
}
let note2 = to.create_note(1, Fs::random(&mut rng), &JUBJUB).unwrap();
let cm2 = Node::new(note2.cm(&JUBJUB).into_repr());
let cm2 = Node::new(note2.cm(&JUBJUB).to_repr());
tree.append(cm2).unwrap();
witness1.append(cm2).unwrap();
let witness2 = IncrementalWitness::from_tree(&tree);
@@ -858,10 +927,15 @@ mod tests {
{
let mut builder = Builder::new(0);
builder
.add_sapling_spend(extsk.clone(), to.diversifier, note1, witness1)
.add_sapling_spend(
extsk.clone(),
*to.diversifier(),
note1,
witness1.path().unwrap(),
)
.unwrap();
builder
.add_sapling_spend(extsk, to.diversifier, note2, witness2)
.add_sapling_spend(extsk, *to.diversifier(), note2, witness2.path().unwrap())
.unwrap();
builder
.add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None)
@@ -872,7 +946,10 @@ mod tests {
Amount::from_u64(20000).unwrap(),
)
.unwrap();
assert_eq!(builder.build(1, MockTxProver), Err(Error::BindingSig))
assert_eq!(
builder.build(consensus::BranchId::Sapling, &MockTxProver),
Err(Error::BindingSig)
)
}
}
}

View File

@@ -1,12 +1,14 @@
//! Structs representing the components within Zcash transactions.
use crate::jubjub::{edwards, Unknown};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use ff::{PrimeField, PrimeFieldRepr};
use ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr, FrRepr};
use std::io::{self, Read, Write};
use legacy::Script;
use redjubjub::{PublicKey, Signature};
use JUBJUB;
use crate::legacy::Script;
use crate::redjubjub::{PublicKey, Signature};
use crate::JUBJUB;
pub mod amount;
pub use self::amount::Amount;
@@ -27,7 +29,7 @@ pub struct OutPoint {
impl OutPoint {
pub fn new(hash: [u8; 32], n: u32) -> Self {
OutPoint {hash, n}
OutPoint { hash, n }
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
@@ -117,7 +119,7 @@ pub struct SpendDescription {
}
impl std::fmt::Debug for SpendDescription {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})",
@@ -136,9 +138,10 @@ impl SpendDescription {
// Consensus rule (§7.3): Canonical encoding is enforced here
let anchor = {
let mut f = FrRepr::default();
f.read_le(&mut reader)?;
Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
let mut f = FrRepr([0; 32]);
reader.read_exact(&mut f.0)?;
Fr::from_repr(f)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "anchor not in field"))?
};
let mut nullifier = [0; 32];
@@ -173,18 +176,16 @@ impl SpendDescription {
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.cv.write(&mut writer)?;
self.anchor.into_repr().write_le(&mut writer)?;
writer.write_all(self.anchor.to_repr().as_ref())?;
writer.write_all(&self.nullifier)?;
self.rk.write(&mut writer)?;
writer.write_all(&self.zkproof)?;
match self.spend_auth_sig {
Some(sig) => sig.write(&mut writer),
None => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Missing spend auth signature",
));
}
None => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Missing spend auth signature",
)),
}
}
}
@@ -199,7 +200,7 @@ pub struct OutputDescription {
}
impl std::fmt::Debug for OutputDescription {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})",
@@ -218,9 +219,10 @@ impl OutputDescription {
// Consensus rule (§7.4): Canonical encoding is enforced here
let cmu = {
let mut f = FrRepr::default();
f.read_le(&mut reader)?;
Fr::from_repr(f).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
let mut f = FrRepr([0; 32]);
reader.read_exact(&mut f.0)?;
Fr::from_repr(f)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cmu not in field"))?
};
// Consensus rules (§4.5):
@@ -252,7 +254,7 @@ impl OutputDescription {
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.cv.write(&mut writer)?;
self.cmu.into_repr().write_le(&mut writer)?;
writer.write_all(self.cmu.to_repr().as_ref())?;
self.ephemeral_key.write(&mut writer)?;
writer.write_all(&self.enc_ciphertext)?;
writer.write_all(&self.out_ciphertext)?;
@@ -266,7 +268,7 @@ enum SproutProof {
}
impl std::fmt::Debug for SproutProof {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
SproutProof::Groth(_) => write!(f, "SproutProof::Groth"),
SproutProof::PHGR(_) => write!(f, "SproutProof::PHGR"),
@@ -288,7 +290,7 @@ pub struct JSDescription {
}
impl std::fmt::Debug for JSDescription {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"JSDescription(
@@ -360,23 +362,20 @@ impl JSDescription {
.map(|mac| reader.read_exact(mac))
.collect::<io::Result<()>>()?;
let proof = match use_groth {
true => {
// Consensus rules (§4.3):
// - Canonical encoding is enforced in librustzcash_sprout_verify()
// - Proof validity is enforced in librustzcash_sprout_verify()
let mut proof = [0; GROTH_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::Groth(proof)
}
false => {
// Consensus rules (§4.3):
// - Canonical encoding is enforced by PHGRProof in zcashd
// - Proof validity is enforced by JSDescription::Verify() in zcashd
let mut proof = [0; PHGR_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::PHGR(proof)
}
let proof = if use_groth {
// Consensus rules (§4.3):
// - Canonical encoding is enforced in librustzcash_sprout_verify()
// - Proof validity is enforced in librustzcash_sprout_verify()
let mut proof = [0; GROTH_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::Groth(proof)
} else {
// Consensus rules (§4.3):
// - Canonical encoding is enforced by PHGRProof in zcashd
// - Proof validity is enforced by JSDescription::Verify() in zcashd
let mut proof = [0; PHGR_PROOF_SIZE];
reader.read_exact(&mut proof)?;
SproutProof::PHGR(proof)
};
let mut ciphertexts = [[0; 601]; ZC_NUM_JS_OUTPUTS];

View File

@@ -206,7 +206,7 @@ mod tests {
#[should_panic]
fn add_panics_on_overflow() {
let v = Amount(MAX_MONEY);
let sum = v + Amount(1);
let _sum = v + Amount(1);
}
#[test]
@@ -220,7 +220,7 @@ mod tests {
#[should_panic]
fn sub_panics_on_underflow() {
let v = Amount(-MAX_MONEY);
let diff = v - Amount(1);
let _diff = v - Amount(1);
}
#[test]

View File

@@ -1,3 +1,5 @@
//! Structs and methods for handling Zcash transactions.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use hex;
use sha2::{Digest, Sha256};
@@ -5,8 +7,8 @@ use std::fmt;
use std::io::{self, Read, Write};
use std::ops::Deref;
use redjubjub::Signature;
use serialize::Vector;
use crate::redjubjub::Signature;
use crate::serialize::Vector;
pub mod builder;
pub mod components;
@@ -28,8 +30,8 @@ const SAPLING_TX_VERSION: u32 = 4;
pub struct TxId(pub [u8; 32]);
impl fmt::Display for TxId {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let mut data = self.0.clone();
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut data = self.0;
data.reverse();
formatter.write_str(&hex::encode(data))
}
@@ -74,7 +76,7 @@ pub struct TransactionData {
}
impl std::fmt::Debug for TransactionData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"TransactionData(
@@ -164,9 +166,10 @@ impl Transaction {
let overwintered = (header >> 31) == 1;
let version = header & 0x7FFFFFFF;
let version_group_id = match overwintered {
true => reader.read_u32::<LittleEndian>()?,
false => 0,
let version_group_id = if overwintered {
reader.read_u32::<LittleEndian>()?
} else {
0
};
let is_overwinter_v3 = overwintered
@@ -185,9 +188,10 @@ impl Transaction {
let vin = Vector::read(&mut reader, TxIn::read)?;
let vout = Vector::read(&mut reader, TxOut::read)?;
let lock_time = reader.read_u32::<LittleEndian>()?;
let expiry_height = match is_overwinter_v3 || is_sapling_v4 {
true => reader.read_u32::<LittleEndian>()?,
false => 0,
let expiry_height = if is_overwinter_v3 || is_sapling_v4 {
reader.read_u32::<LittleEndian>()?
} else {
0
};
let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 {
@@ -223,9 +227,10 @@ impl Transaction {
};
let binding_sig =
match is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
true => Some(Signature::read(&mut reader)?),
false => None,
if is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
Some(Signature::read(&mut reader)?)
} else {
None
};
Transaction::from_data(TransactionData {

View File

@@ -1,21 +1,21 @@
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, WriteBytesExt};
use ff::{PrimeField, PrimeFieldRepr};
use ff::PrimeField;
use super::{
components::{Amount, TxOut},
Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION,
SAPLING_VERSION_GROUP_ID,
};
use legacy::Script;
use crate::{consensus, legacy::Script};
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &'static [u8; 12] = b"ZcashSigHash";
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashPrevoutHash";
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSequencHash";
const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashOutputsHash";
const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashJSplitsHash";
const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSSpendsHash";
const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSOutputHash";
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash";
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash";
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSequencHash";
const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashOutputsHash";
const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashJSplitsHash";
const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSSpendsHash";
const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSOutputHash";
pub const SIGHASH_ALL: u32 = 1;
const SIGHASH_NONE: u32 = 2;
@@ -128,7 +128,7 @@ fn shielded_spends_hash(tx: &TransactionData) -> Blake2bHash {
let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384);
for s_spend in &tx.shielded_spends {
s_spend.cv.write(&mut data).unwrap();
s_spend.anchor.into_repr().write_le(&mut data).unwrap();
data.extend_from_slice(s_spend.anchor.to_repr().as_ref());
data.extend_from_slice(&s_spend.nullifier);
s_spend.rk.write(&mut data).unwrap();
data.extend_from_slice(&s_spend.zkproof);
@@ -152,7 +152,7 @@ fn shielded_outputs_hash(tx: &TransactionData) -> Blake2bHash {
pub fn signature_hash_data(
tx: &TransactionData,
consensus_branch_id: u32,
consensus_branch_id: consensus::BranchId,
hash_type: u32,
transparent_input: Option<(usize, &Script, Amount)>,
) -> Vec<u8> {
@@ -162,7 +162,7 @@ pub fn signature_hash_data(
let mut personal = [0; 16];
(&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX);
(&mut personal[12..])
.write_u32::<LittleEndian>(consensus_branch_id)
.write_u32::<LittleEndian>(consensus_branch_id.into())
.unwrap();
let mut h = Blake2bParams::new()
@@ -230,7 +230,7 @@ pub fn signature_hash_data(
pub fn signature_hash(
tx: &Transaction,
consensus_branch_id: u32,
consensus_branch_id: consensus::BranchId,
hash_type: u32,
transparent_input: Option<(usize, &Script, Amount)>,
) -> Vec<u8> {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,13 @@
//! Implementation of [ZIP 32] for hierarchical deterministic key management.
//!
//! [ZIP 32]: https://zips.z.cash/zip-0032
use aes::Aes256;
use blake2b_simd::Params as Blake2bParams;
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
use ff::Field;
use fpe::ff1::{BinaryNumeralString, FF1};
use pairing::bls12_381::Bls12;
use std::ops::AddAssign;
use crate::{
jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform},
@@ -16,8 +20,8 @@ use crate::{
JUBJUB,
};
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling";
pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSaplingFVFP";
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP";
// Common helper functions
@@ -83,9 +87,9 @@ impl ChildIndex {
}
fn to_index(&self) -> u32 {
match self {
&ChildIndex::Hardened(i) => i + (1 << 31),
&ChildIndex::NonHardened(i) => i,
match *self {
ChildIndex::Hardened(i) => i + (1 << 31),
ChildIndex::NonHardened(i) => i,
}
}
}
@@ -198,7 +202,7 @@ impl std::cmp::PartialEq for ExtendedSpendingKey {
}
impl std::fmt::Debug for ExtendedSpendingKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})",
@@ -221,7 +225,7 @@ impl std::cmp::PartialEq for ExtendedFullViewingKey {
}
impl std::fmt::Debug for ExtendedFullViewingKey {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})",
@@ -434,7 +438,7 @@ impl ExtendedFullViewingKey {
Ok(ret) => ret,
Err(()) => return Err(()),
};
match self.fvk.vk.into_payment_address(d_j, &JUBJUB) {
match self.fvk.vk.to_payment_address(d_j, &JUBJUB) {
Some(addr) => Ok((j, addr)),
None => Err(()),
}
@@ -449,7 +453,7 @@ impl ExtendedFullViewingKey {
mod tests {
use super::*;
use ff::{PrimeField, PrimeFieldRepr};
use ff::PrimeField;
#[test]
fn derive_nonhardened_child() {
@@ -548,7 +552,7 @@ mod tests {
let (j_m, addr_m) = xsk_m.default_address().unwrap();
assert_eq!(j_m.0, [0; 11]);
assert_eq!(
addr_m.diversifier.0,
addr_m.diversifier().0,
// Computed using this Rust implementation
[59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
);
@@ -1010,11 +1014,8 @@ mod tests {
let xsk = &xsks[j];
let tv = &test_vectors[j];
let mut buf = [0; 32];
xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap();
assert_eq!(buf, tv.ask.unwrap());
xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap();
assert_eq!(buf, tv.nsk.unwrap());
assert_eq!(xsk.expsk.ask.to_repr().as_ref(), tv.ask.unwrap());
assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap());
assert_eq!(xsk.expsk.ovk.0, tv.ovk);
assert_eq!(xsk.dk.0, tv.dk);
@@ -1039,13 +1040,7 @@ mod tests {
assert_eq!(xfvk.dk.0, tv.dk);
assert_eq!(xfvk.chain_code.0, tv.c);
xfvk.fvk
.vk
.ivk()
.into_repr()
.write_le(&mut buf[..])
.unwrap();
assert_eq!(buf, tv.ivk);
assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk);
let mut ser = vec![];
xfvk.write(&mut ser).unwrap();