zebra_chain/sapling/note/
nullifiers.rs

1//! Sapling nullifiers.
2
3use crate::fmt::HexDebug;
4
5/// A Nullifier for Sapling transactions
6#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
7#[cfg_attr(
8    any(test, feature = "proptest-impl"),
9    derive(proptest_derive::Arbitrary)
10)]
11pub struct Nullifier(pub HexDebug<[u8; 32]>);
12
13impl From<[u8; 32]> for Nullifier {
14    fn from(buf: [u8; 32]) -> Self {
15        Self(buf.into())
16    }
17}
18
19impl From<Nullifier> for [u8; 32] {
20    fn from(n: Nullifier) -> Self {
21        *n.0
22    }
23}
24
25impl From<Nullifier> for [jubjub::Fq; 2] {
26    /// Add the nullifier through multiscalar packing
27    ///
28    /// Informed by <https://github.com/zkcrypto/bellman/blob/main/src/gadgets/multipack.rs>
29    fn from(n: Nullifier) -> Self {
30        use std::ops::AddAssign;
31
32        let nullifier_bits_le: Vec<bool> =
33            n.0.iter()
34                .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
35                .collect();
36
37        // The number of bits needed to represent the modulus, minus 1.
38        const CAPACITY: usize = 255 - 1;
39
40        let mut result = [jubjub::Fq::zero(); 2];
41
42        // Since we know the max bits of the input (256) and the chunk size
43        // (254), this will always result in 2 chunks.
44        for (i, bits) in nullifier_bits_le.chunks(CAPACITY).enumerate() {
45            let mut cur = jubjub::Fq::zero();
46            let mut coeff = jubjub::Fq::one();
47
48            for bit in bits {
49                if *bit {
50                    cur.add_assign(&coeff);
51                }
52
53                coeff = coeff.double();
54            }
55
56            result[i] = cur
57        }
58
59        result
60    }
61}