sapling_crypto_ce/primitives/
mod.rs

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