Skip to main content

fil_sapling_crypto/primitives/
mod.rs

1use fff::{Field, PrimeField, PrimeFieldRepr};
2
3use crate::constants;
4
5use crate::group_hash::group_hash;
6
7use crate::pedersen_hash::{pedersen_hash, Personalization};
8
9use byteorder::{LittleEndian, WriteBytesExt};
10
11use crate::jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder};
12
13use blake2s_simd::Params;
14
15#[derive(Clone)]
16pub struct ValueCommitment<E: JubjubEngine> {
17    pub value: u64,
18    pub randomness: E::Fs,
19}
20
21impl<E: JubjubEngine> ValueCommitment<E> {
22    pub fn cm(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
23        params
24            .generator(FixedGenerators::ValueCommitmentValue)
25            .mul(self.value, params)
26            .add(
27                &params
28                    .generator(FixedGenerators::ValueCommitmentRandomness)
29                    .mul(self.randomness, params),
30                params,
31            )
32    }
33}
34
35#[derive(Clone)]
36pub struct ProofGenerationKey<E: JubjubEngine> {
37    pub ak: edwards::Point<E, PrimeOrder>,
38    pub nsk: E::Fs,
39}
40
41impl<E: JubjubEngine> ProofGenerationKey<E> {
42    pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey<E> {
43        ViewingKey {
44            ak: self.ak.clone(),
45            nk: params
46                .generator(FixedGenerators::ProofGenerationKey)
47                .mul(self.nsk, params),
48        }
49    }
50}
51
52pub struct ViewingKey<E: JubjubEngine> {
53    pub ak: edwards::Point<E, PrimeOrder>,
54    pub nk: edwards::Point<E, PrimeOrder>,
55}
56
57impl<E: JubjubEngine> ViewingKey<E> {
58    pub fn rk(&self, ar: E::Fs, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
59        self.ak.add(
60            &params
61                .generator(FixedGenerators::SpendingKeyGenerator)
62                .mul(ar, params),
63            params,
64        )
65    }
66
67    pub fn ivk(&self) -> E::Fs {
68        let mut preimage = [0; 64];
69
70        self.ak.write(&mut preimage[0..32]).unwrap();
71        self.nk.write(&mut preimage[32..64]).unwrap();
72
73        let mut params = Params::new();
74        params.hash_length(32);
75        params.personal(constants::CRH_IVK_PERSONALIZATION);
76        params.key(&[]);
77        params.salt(&[]);
78
79        let mut h = params.to_state();
80        h.update(&preimage);
81        let mut h = h.finalize().as_ref().to_vec();
82
83        // Drop the most significant five bits, so it can be interpreted as a scalar.
84        h[31] &= 0b0000_0111;
85
86        let mut e = <E::Fs as PrimeField>::Repr::default();
87        e.read_le(&h[..]).unwrap();
88
89        E::Fs::from_repr(e).expect("should be a valid scalar")
90    }
91
92    pub fn into_payment_address(
93        &self,
94        diversifier: Diversifier,
95        params: &E::Params,
96    ) -> Option<PaymentAddress<E>> {
97        diversifier.g_d(params).map(|g_d| {
98            let pk_d = g_d.mul(self.ivk(), params);
99
100            PaymentAddress {
101                pk_d: pk_d,
102                diversifier: diversifier,
103            }
104        })
105    }
106}
107
108#[derive(Copy, Clone)]
109pub struct Diversifier(pub [u8; 11]);
110
111impl Diversifier {
112    pub fn g_d<E: JubjubEngine>(
113        &self,
114        params: &E::Params,
115    ) -> Option<edwards::Point<E, PrimeOrder>> {
116        group_hash::<E>(
117            &self.0,
118            constants::KEY_DIVERSIFICATION_PERSONALIZATION,
119            params,
120        )
121    }
122}
123
124#[derive(Clone)]
125pub struct PaymentAddress<E: JubjubEngine> {
126    pub pk_d: edwards::Point<E, PrimeOrder>,
127    pub diversifier: Diversifier,
128}
129
130impl<E: JubjubEngine> PaymentAddress<E> {
131    pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> {
132        self.diversifier.g_d(params)
133    }
134
135    pub fn create_note(
136        &self,
137        value: u64,
138        randomness: E::Fs,
139        params: &E::Params,
140    ) -> Option<Note<E>> {
141        self.g_d(params).map(|g_d| Note {
142            value: value,
143            r: randomness,
144            g_d: g_d,
145            pk_d: self.pk_d.clone(),
146        })
147    }
148}
149
150pub struct Note<E: JubjubEngine> {
151    /// The value of the note
152    pub value: u64,
153    /// The diversified base of the address, GH(d)
154    pub g_d: edwards::Point<E, PrimeOrder>,
155    /// The public key of the address, g_d^ivk
156    pub pk_d: edwards::Point<E, PrimeOrder>,
157    /// The commitment randomness
158    pub r: E::Fs,
159}
160
161impl<E: JubjubEngine> Note<E> {
162    pub fn uncommitted() -> E::Fr {
163        // The smallest u-coordinate that is not on the curve
164        // is one.
165        // TODO: This should be relocated to JubjubEngine as
166        // it's specific to the curve we're using, not all
167        // twisted edwards curves.
168        E::Fr::one()
169    }
170
171    /// Computes the note commitment, returning the full point.
172    fn cm_full_point(&self, params: &E::Params) -> edwards::Point<E, PrimeOrder> {
173        // Calculate the note contents, as bytes
174        let mut note_contents = vec![];
175
176        // Writing the value in little endian
177        (&mut note_contents)
178            .write_u64::<LittleEndian>(self.value)
179            .unwrap();
180
181        // Write g_d
182        self.g_d.write(&mut note_contents).unwrap();
183
184        // Write pk_d
185        self.pk_d.write(&mut note_contents).unwrap();
186
187        assert_eq!(note_contents.len(), 32 + 32 + 8);
188
189        // Compute the Pedersen hash of the note contents
190        let hash_of_contents = pedersen_hash(
191            Personalization::NoteCommitment,
192            note_contents
193                .into_iter()
194                .flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
195            params,
196        );
197
198        // Compute final commitment
199        params
200            .generator(FixedGenerators::NoteCommitmentRandomness)
201            .mul(self.r, params)
202            .add(&hash_of_contents, params)
203    }
204
205    /// Computes the nullifier given the viewing key and
206    /// note position
207    pub fn nf(&self, viewing_key: &ViewingKey<E>, position: u64, params: &E::Params) -> Vec<u8> {
208        // Compute rho = cm + position.G
209        let rho = self.cm_full_point(params).add(
210            &params
211                .generator(FixedGenerators::NullifierPosition)
212                .mul(position, params),
213            params,
214        );
215
216        // Compute nf = BLAKE2s(nk | rho)
217        let mut nf_preimage = [0u8; 64];
218        viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap();
219        rho.write(&mut nf_preimage[32..64]).unwrap();
220
221        let mut params = Params::new();
222        params.hash_length(32);
223        params.personal(constants::PRF_NF_PERSONALIZATION);
224        params.key(&[]);
225        params.salt(&[]);
226
227        let mut h = params.to_state();
228        h.update(&nf_preimage);
229        h.finalize().as_ref().to_vec()
230    }
231
232    /// Computes the note commitment
233    pub fn cm(&self, params: &E::Params) -> E::Fr {
234        // The commitment is in the prime order subgroup, so mapping the
235        // commitment to the x-coordinate is an injective encoding.
236        self.cm_full_point(params).into_xy().0
237    }
238}