1pub mod group_hash;
4pub mod keys;
5pub mod note_encryption;
6pub mod pedersen_hash;
7pub mod prover;
8pub mod redjubjub;
9pub mod util;
10
11use bitvec::{order::Lsb0, view::AsBits};
12use blake2s_simd::Params as Blake2sParams;
13use byteorder::{LittleEndian, WriteBytesExt};
14use ff::{Field, PrimeField};
15use group::{Curve, Group, GroupEncoding};
16use incrementalmerkletree::{self, Altitude};
17use lazy_static::lazy_static;
18use rand_core::{CryptoRng, RngCore};
19use std::array::TryFromSliceError;
20use std::convert::{TryFrom, TryInto};
21use std::io::{self, Read, Write};
22use subtle::{Choice, ConstantTimeEq};
23
24use crate::{
25 constants::{self, SPENDING_KEY_GENERATOR},
26 keys::prf_expand,
27 merkle_tree::{HashSer, Hashable},
28 transaction::components::amount::MAX_MONEY,
29};
30
31use self::{
32 group_hash::group_hash,
33 pedersen_hash::{pedersen_hash, Personalization},
34 redjubjub::{PrivateKey, PublicKey, Signature},
35};
36
37pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
38pub const SAPLING_COMMITMENT_TREE_DEPTH_U8: u8 = 32;
39
40pub fn merkle_hash(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> [u8; 32] {
42 let lhs = {
43 let mut tmp = [false; 256];
44 for (a, b) in tmp.iter_mut().zip(lhs.as_bits::<Lsb0>()) {
45 *a = *b;
46 }
47 tmp
48 };
49
50 let rhs = {
51 let mut tmp = [false; 256];
52 for (a, b) in tmp.iter_mut().zip(rhs.as_bits::<Lsb0>()) {
53 *a = *b;
54 }
55 tmp
56 };
57
58 ironfish_jubjub::ExtendedPoint::from(pedersen_hash(
59 Personalization::MerkleTree(depth),
60 lhs.iter()
61 .copied()
62 .take(blstrs::Scalar::NUM_BITS as usize)
63 .chain(
64 rhs.iter()
65 .copied()
66 .take(blstrs::Scalar::NUM_BITS as usize),
67 ),
68 ))
69 .to_affine()
70 .get_u()
71 .to_repr()
72}
73
74#[derive(Clone, Copy, Debug, PartialEq)]
76pub struct Node {
77 repr: [u8; 32],
78}
79
80impl Node {
81 pub fn new(repr: [u8; 32]) -> Self {
82 Node { repr }
83 }
84}
85
86impl incrementalmerkletree::Hashable for Node {
87 fn empty_leaf() -> Self {
88 Node {
89 repr: Note::uncommitted().to_repr(),
90 }
91 }
92
93 fn combine(altitude: Altitude, lhs: &Self, rhs: &Self) -> Self {
94 Node {
95 repr: merkle_hash(altitude.into(), &lhs.repr, &rhs.repr),
96 }
97 }
98
99 fn empty_root(altitude: Altitude) -> Self {
100 EMPTY_ROOTS[<usize>::from(altitude)]
101 }
102}
103
104impl HashSer for Node {
105 fn read<R: Read>(mut reader: R) -> io::Result<Self> {
106 let mut repr = [0u8; 32];
107 reader.read_exact(&mut repr)?;
108 Ok(Node::new(repr))
109 }
110
111 fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
112 writer.write_all(self.repr.as_ref())
113 }
114}
115
116impl From<Node> for blstrs::Scalar {
117 fn from(node: Node) -> Self {
118 blstrs::Scalar::from_repr(node.repr).unwrap()
120 }
121}
122
123lazy_static! {
124 static ref EMPTY_ROOTS: Vec<Node> = {
125 let mut v = vec![Node::blank()];
126 for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
127 let next = Node::combine(d, &v[d], &v[d]);
128 v.push(next);
129 }
130 v
131 };
132}
133
134pub fn spend_sig<R: RngCore + CryptoRng>(
136 ask: PrivateKey,
137 ar: ironfish_jubjub::Fr,
138 sighash: &[u8; 32],
139 rng: &mut R,
140) -> Signature {
141 spend_sig_internal(ask, ar, sighash, rng)
142}
143
144pub(crate) fn spend_sig_internal<R: RngCore>(
145 ask: PrivateKey,
146 ar: ironfish_jubjub::Fr,
147 sighash: &[u8; 32],
148 rng: &mut R,
149) -> Signature {
150 let rsk = ask.randomize(ar);
152
153 let rk = PublicKey::from_private(&rsk, *SPENDING_KEY_GENERATOR);
155
156 let mut data_to_be_signed = [0u8; 64];
158 data_to_be_signed[0..32].copy_from_slice(&rk.0.to_bytes());
159 (&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]);
160
161 rsk.sign(&data_to_be_signed, rng, *SPENDING_KEY_GENERATOR)
163}
164
165#[derive(Clone)]
166pub struct ValueCommitment {
167 pub value: u64,
168 pub randomness: ironfish_jubjub::Fr,
169}
170
171impl ValueCommitment {
172 pub fn commitment(&self) -> ironfish_jubjub::SubgroupPoint {
173 (*constants::VALUE_COMMITMENT_VALUE_GENERATOR * ironfish_jubjub::Fr::from(self.value))
174 + (*constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness)
175 }
176}
177
178#[derive(Clone)]
179pub struct ProofGenerationKey {
180 pub ak: ironfish_jubjub::SubgroupPoint,
181 pub nsk: ironfish_jubjub::Fr,
182}
183
184impl ProofGenerationKey {
185 pub fn to_viewing_key(&self) -> ViewingKey {
186 ViewingKey {
187 ak: self.ak,
188 nk: *constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk,
189 }
190 }
191}
192
193#[derive(Debug, Clone)]
194pub struct ViewingKey {
195 pub ak: ironfish_jubjub::SubgroupPoint,
196 pub nk: ironfish_jubjub::SubgroupPoint,
197}
198
199impl ViewingKey {
200 pub fn rk(&self, ar: ironfish_jubjub::Fr) -> ironfish_jubjub::SubgroupPoint {
201 self.ak + *constants::SPENDING_KEY_GENERATOR * ar
202 }
203
204 pub fn ivk(&self) -> SaplingIvk {
205 let mut h = [0; 32];
206 h.copy_from_slice(
207 Blake2sParams::new()
208 .hash_length(32)
209 .personal(constants::CRH_IVK_PERSONALIZATION)
210 .to_state()
211 .update(&self.ak.to_bytes())
212 .update(&self.nk.to_bytes())
213 .finalize()
214 .as_bytes(),
215 );
216
217 h[31] &= 0b0000_0111;
219
220 SaplingIvk(ironfish_jubjub::Fr::from_repr(h).unwrap())
221 }
222
223 pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
224 self.ivk().to_payment_address(diversifier)
225 }
226}
227
228#[derive(Debug, Clone)]
229pub struct SaplingIvk(pub ironfish_jubjub::Fr);
230
231impl SaplingIvk {
232 pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
233 diversifier.g_d().and_then(|g_d| {
234 let pk_d = g_d * self.0;
235
236 PaymentAddress::from_parts(diversifier, pk_d)
237 })
238 }
239
240 pub fn to_repr(&self) -> [u8; 32] {
241 self.0.to_repr()
242 }
243}
244
245#[derive(Copy, Clone, Debug, PartialEq)]
246pub struct Diversifier(pub [u8; 11]);
247
248impl Diversifier {
249 pub fn g_d(&self) -> Option<ironfish_jubjub::SubgroupPoint> {
250 group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION)
251 }
252}
253
254#[derive(Clone, Debug)]
261pub struct PaymentAddress {
262 pk_d: ironfish_jubjub::SubgroupPoint,
263 diversifier: Diversifier,
264}
265
266impl PartialEq for PaymentAddress {
267 fn eq(&self, other: &Self) -> bool {
268 self.pk_d == other.pk_d && self.diversifier == other.diversifier
269 }
270}
271
272impl PaymentAddress {
273 pub fn from_parts(diversifier: Diversifier, pk_d: ironfish_jubjub::SubgroupPoint) -> Option<Self> {
277 if pk_d.is_identity().into() {
278 None
279 } else {
280 Some(PaymentAddress { pk_d, diversifier })
281 }
282 }
283
284 #[cfg(test)]
288 pub(crate) fn from_parts_unchecked(
289 diversifier: Diversifier,
290 pk_d: ironfish_jubjub::SubgroupPoint,
291 ) -> Self {
292 PaymentAddress { pk_d, diversifier }
293 }
294
295 pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
297 let diversifier = {
298 let mut tmp = [0; 11];
299 tmp.copy_from_slice(&bytes[0..11]);
300 Diversifier(tmp)
301 };
302 diversifier.g_d()?;
304
305 let pk_d = ironfish_jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap());
306 if pk_d.is_some().into() {
307 PaymentAddress::from_parts(diversifier, pk_d.unwrap())
308 } else {
309 None
310 }
311 }
312
313 pub fn to_bytes(&self) -> [u8; 43] {
315 let mut bytes = [0; 43];
316 bytes[0..11].copy_from_slice(&self.diversifier.0);
317 bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
318 bytes
319 }
320
321 pub fn diversifier(&self) -> &Diversifier {
323 &self.diversifier
324 }
325
326 pub fn pk_d(&self) -> &ironfish_jubjub::SubgroupPoint {
328 &self.pk_d
329 }
330
331 pub fn g_d(&self) -> Option<ironfish_jubjub::SubgroupPoint> {
332 self.diversifier.g_d()
333 }
334
335 pub fn create_note(&self, value: u64, rseed: Rseed) -> Option<Note> {
336 self.g_d().map(|g_d| Note {
337 value,
338 rseed,
339 g_d,
340 pk_d: self.pk_d,
341 })
342 }
343}
344
345#[derive(Copy, Clone, Debug)]
351pub enum Rseed {
352 BeforeZip212(ironfish_jubjub::Fr),
353 AfterZip212([u8; 32]),
354}
355
356#[derive(Copy, Clone, Debug, PartialEq, Eq)]
358pub struct Nullifier(pub [u8; 32]);
359
360impl Nullifier {
361 pub fn from_slice(bytes: &[u8]) -> Result<Nullifier, TryFromSliceError> {
362 bytes.try_into().map(Nullifier)
363 }
364
365 pub fn to_vec(&self) -> Vec<u8> {
366 self.0.to_vec()
367 }
368}
369impl AsRef<[u8]> for Nullifier {
370 fn as_ref(&self) -> &[u8] {
371 &self.0
372 }
373}
374
375impl ConstantTimeEq for Nullifier {
376 fn ct_eq(&self, other: &Self) -> Choice {
377 self.0.ct_eq(&other.0)
378 }
379}
380
381#[derive(Clone, Copy, Debug, PartialEq)]
382pub struct NoteValue(u64);
383
384impl TryFrom<u64> for NoteValue {
385 type Error = ();
386
387 fn try_from(value: u64) -> Result<Self, Self::Error> {
388 if value <= MAX_MONEY as u64 {
389 Ok(NoteValue(value))
390 } else {
391 Err(())
392 }
393 }
394}
395
396impl From<NoteValue> for u64 {
397 fn from(value: NoteValue) -> u64 {
398 value.0
399 }
400}
401
402#[derive(Clone, Debug)]
403pub struct Note {
404 pub value: u64,
406 pub g_d: ironfish_jubjub::SubgroupPoint,
408 pub pk_d: ironfish_jubjub::SubgroupPoint,
410 pub rseed: Rseed,
412}
413
414impl PartialEq for Note {
415 fn eq(&self, other: &Self) -> bool {
416 self.value == other.value
417 && self.g_d == other.g_d
418 && self.pk_d == other.pk_d
419 && self.rcm() == other.rcm()
420 }
421}
422
423impl Note {
424 pub fn uncommitted() -> blstrs::Scalar {
425 blstrs::Scalar::one()
428 }
429
430 fn cm_full_point(&self) -> ironfish_jubjub::SubgroupPoint {
432 let mut note_contents = vec![];
434
435 (&mut note_contents)
437 .write_u64::<LittleEndian>(self.value)
438 .unwrap();
439
440 note_contents.extend_from_slice(&self.g_d.to_bytes());
442
443 note_contents.extend_from_slice(&self.pk_d.to_bytes());
445
446 assert_eq!(note_contents.len(), 32 + 32 + 8);
447
448 let hash_of_contents = pedersen_hash(
450 Personalization::NoteCommitment,
451 note_contents
452 .into_iter()
453 .flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
454 );
455
456 (*constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents
458 }
459
460 pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Nullifier {
463 let rho = self.cm_full_point()
465 + (*constants::NULLIFIER_POSITION_GENERATOR * ironfish_jubjub::Fr::from(position));
466
467 Nullifier::from_slice(
469 Blake2sParams::new()
470 .hash_length(32)
471 .personal(constants::PRF_NF_PERSONALIZATION)
472 .to_state()
473 .update(&viewing_key.nk.to_bytes())
474 .update(&rho.to_bytes())
475 .finalize()
476 .as_bytes(),
477 )
478 .unwrap()
479 }
480
481 pub fn cmu(&self) -> blstrs::Scalar {
483 ironfish_jubjub::ExtendedPoint::from(self.cm_full_point())
486 .to_affine()
487 .get_u()
488 }
489
490 pub fn rcm(&self) -> ironfish_jubjub::Fr {
491 match self.rseed {
492 Rseed::BeforeZip212(rcm) => rcm,
493 Rseed::AfterZip212(rseed) => {
494 ironfish_jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array())
495 }
496 }
497 }
498
499 pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> ironfish_jubjub::Fr {
500 self.generate_or_derive_esk_internal(rng)
501 }
502
503 pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(&self, rng: &mut R) -> ironfish_jubjub::Fr {
504 match self.derive_esk() {
505 None => ironfish_jubjub::Fr::random(rng),
506 Some(esk) => esk,
507 }
508 }
509
510 pub fn derive_esk(&self) -> Option<ironfish_jubjub::Fr> {
512 match self.rseed {
513 Rseed::BeforeZip212(_) => None,
514 Rseed::AfterZip212(rseed) => Some(ironfish_jubjub::Fr::from_bytes_wide(
515 prf_expand(&rseed, &[0x05]).as_array(),
516 )),
517 }
518 }
519}
520
521#[cfg(any(test, feature = "test-dependencies"))]
522pub mod testing {
523 use proptest::prelude::*;
524 use std::cmp::min;
525 use std::convert::TryFrom;
526
527 use crate::{
528 transaction::components::amount::MAX_MONEY, zip32::testing::arb_extended_spending_key,
529 };
530
531 use super::{Node, Note, NoteValue, PaymentAddress, Rseed};
532
533 prop_compose! {
534 pub fn arb_note_value()(value in 0u64..=MAX_MONEY as u64) -> NoteValue {
535 NoteValue::try_from(value).unwrap()
536 }
537 }
538
539 prop_compose! {
540 pub fn arb_positive_note_value(bound: u64)(
542 value in 1u64..=(min(bound, MAX_MONEY as u64))
543 ) -> NoteValue {
544 NoteValue::try_from(value).unwrap()
545 }
546 }
547
548 pub fn arb_payment_address() -> impl Strategy<Value = PaymentAddress> {
549 arb_extended_spending_key().prop_map(|sk| sk.default_address().1)
550 }
551
552 prop_compose! {
553 pub fn arb_node()(value in prop::array::uniform32(prop::num::u8::ANY)) -> Node {
554 Node::new(value)
555 }
556 }
557
558 prop_compose! {
559 pub fn arb_note(value: NoteValue)(
560 addr in arb_payment_address(),
561 rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212)
562 ) -> Note {
563 Note {
564 value: value.into(),
565 g_d: addr.g_d().unwrap(), pk_d: *addr.pk_d(),
567 rseed
568 }
569 }
570 }
571}