phoenix_core/
note.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use core::convert::{TryFrom, TryInto};
8
9use dusk_bls12_381::BlsScalar;
10use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
11use dusk_jubjub::{dhke, JubJubAffine, JubJubScalar, GENERATOR_NUMS_EXTENDED};
12use dusk_poseidon::{Domain, Hash};
13use ff::Field;
14use jubjub_elgamal as elgamal;
15use jubjub_schnorr::{PublicKey as NotePublicKey, SecretKey as NoteSecretKey};
16use rand::{CryptoRng, RngCore};
17
18use crate::{
19    aes, transparent_value_commitment, value_commitment, Error, PublicKey,
20    SecretKey, StealthAddress, ViewKey,
21};
22
23#[cfg(feature = "rkyv-impl")]
24use rkyv::{Archive, Deserialize, Serialize};
25
26/// Blinder used for transparent notes.
27pub(crate) const TRANSPARENT_BLINDER: JubJubScalar = JubJubScalar::zero();
28
29/// Size of the Phoenix notes plaintext: value (8 bytes) + blinder (32 bytes)
30pub(crate) const PLAINTEXT_SIZE: usize = 40;
31
32/// Size of the Phoenix notes value_enc
33pub const VALUE_ENC_SIZE: usize = PLAINTEXT_SIZE + aes::ENCRYPTION_EXTRA_SIZE;
34
35/// The types of a Note
36#[derive(Debug, Clone, Copy, Eq, PartialEq)]
37#[cfg_attr(
38    feature = "rkyv-impl",
39    derive(Archive, Serialize, Deserialize),
40    archive_attr(derive(bytecheck::CheckBytes))
41)]
42pub enum NoteType {
43    /// Defines a Transparent type of Note
44    Transparent = 0,
45    /// Defines an Obfuscated type of Note
46    Obfuscated = 1,
47}
48
49impl TryFrom<u8> for NoteType {
50    type Error = Error;
51
52    fn try_from(note_type: u8) -> Result<Self, Self::Error> {
53        match note_type {
54            0 => Ok(NoteType::Transparent),
55            1 => Ok(NoteType::Obfuscated),
56            n => Err(Error::InvalidNoteType(n)),
57        }
58    }
59}
60
61impl TryFrom<i32> for NoteType {
62    type Error = Error;
63
64    fn try_from(note_type: i32) -> Result<Self, Self::Error> {
65        (note_type as u8).try_into()
66    }
67}
68
69/// Phoenix Note struct
70#[derive(Clone, Debug, Eq)]
71#[cfg_attr(
72    feature = "rkyv-impl",
73    derive(Archive, Serialize, Deserialize),
74    archive_attr(derive(bytecheck::CheckBytes))
75)]
76pub struct Note {
77    pub(crate) note_type: NoteType,
78    pub(crate) value_commitment: JubJubAffine,
79    pub(crate) stealth_address: StealthAddress,
80    pub(crate) pos: u64,
81    pub(crate) value_enc: [u8; VALUE_ENC_SIZE],
82    pub(crate) sender: Sender,
83}
84
85impl PartialEq for Note {
86    fn eq(&self, other: &Self) -> bool {
87        self.value_enc == other.value_enc
88            && self.sender == other.sender
89            && self.hash() == other.hash()
90    }
91}
92
93impl Note {
94    /// Creates a new phoenix output note
95    pub fn new<R: RngCore + CryptoRng>(
96        rng: &mut R,
97        note_type: NoteType,
98        sender_pk: &PublicKey,
99        receiver_pk: &PublicKey,
100        value: u64,
101        value_blinder: JubJubScalar,
102        sender_blinder: [JubJubScalar; 2],
103    ) -> Self {
104        let r = JubJubScalar::random(&mut *rng);
105        let stealth_address = receiver_pk.gen_stealth_address(&r);
106
107        let value_commitment = value_commitment(value, value_blinder);
108
109        // Output notes have undefined position, equals to u64's MAX value
110        let pos = u64::MAX;
111
112        let value_enc = match note_type {
113            NoteType::Transparent => {
114                let mut value_enc = [0u8; VALUE_ENC_SIZE];
115                value_enc[..u64::SIZE].copy_from_slice(&value.to_bytes());
116
117                value_enc
118            }
119            NoteType::Obfuscated => {
120                let shared_secret = dhke(&r, receiver_pk.A());
121                let value_blinder = BlsScalar::from(value_blinder);
122
123                let mut plaintext = value.to_bytes().to_vec();
124                plaintext.append(&mut value_blinder.to_bytes().to_vec());
125
126                let salt = stealth_address.to_bytes();
127
128                aes::encrypt(&shared_secret, &salt, &plaintext, rng)
129                    .expect("Encrypted correctly.")
130            }
131        };
132
133        Note {
134            note_type,
135            value_commitment,
136            stealth_address,
137            pos,
138            value_enc,
139            sender: Sender::encrypt(
140                stealth_address.note_pk(),
141                sender_pk,
142                &sender_blinder,
143            ),
144        }
145    }
146
147    /// Creates a new transparent note
148    ///
149    /// The blinding factor will be constant zero since the value commitment
150    /// exists only to shield the value. The value is not hidden for transparent
151    /// notes, so this can be trivially treated as a constant.
152    pub fn transparent<R: RngCore + CryptoRng>(
153        rng: &mut R,
154        sender_pk: &PublicKey,
155        receiver_pk: &PublicKey,
156        value: u64,
157        sender_blinder: [JubJubScalar; 2],
158    ) -> Self {
159        Self::new(
160            rng,
161            NoteType::Transparent,
162            sender_pk,
163            receiver_pk,
164            value,
165            TRANSPARENT_BLINDER,
166            sender_blinder,
167        )
168    }
169
170    /// Creates a new transparent note
171    ///
172    /// This is equivalent to [`transparent`] but taking only a stealth address
173    /// and a value. This is done to be able to generate a note
174    /// directly for a stealth address, as opposed to a public key.
175    pub fn transparent_stealth(
176        stealth_address: StealthAddress,
177        value: u64,
178        sender: impl Into<Sender>,
179    ) -> Self {
180        let value_commitment = transparent_value_commitment(value);
181
182        let pos = u64::MAX;
183
184        let mut value_enc = [0u8; VALUE_ENC_SIZE];
185        value_enc[..u64::SIZE].copy_from_slice(&value.to_bytes());
186
187        Note {
188            note_type: NoteType::Transparent,
189            value_commitment,
190            stealth_address,
191            pos,
192            value_enc,
193            sender: sender.into(),
194        }
195    }
196
197    /// Creates a new obfuscated note
198    ///
199    /// The provided blinding factor will be used to calculate the value
200    /// commitment of the note. The tuple (value, value_blinder), known by
201    /// the caller of this function, must be later used to prove the
202    /// knowledge of the value commitment of this note.
203    pub fn obfuscated<R: RngCore + CryptoRng>(
204        rng: &mut R,
205        sender_pk: &PublicKey,
206        receiver_pk: &PublicKey,
207        value: u64,
208        value_blinder: JubJubScalar,
209        sender_blinder: [JubJubScalar; 2],
210    ) -> Self {
211        Self::new(
212            rng,
213            NoteType::Obfuscated,
214            sender_pk,
215            receiver_pk,
216            value,
217            value_blinder,
218            sender_blinder,
219        )
220    }
221
222    /// Creates a new empty [`Note`]
223    pub fn empty() -> Self {
224        Self {
225            note_type: NoteType::Transparent,
226            value_commitment: JubJubAffine::default(),
227            stealth_address: StealthAddress::default(),
228            pos: 0,
229            value_enc: [0; VALUE_ENC_SIZE],
230            sender: Sender::Encryption(
231                [(JubJubAffine::default(), JubJubAffine::default()); 2],
232            ),
233        }
234    }
235
236    fn decrypt_value(
237        &self,
238        vk: &ViewKey,
239    ) -> Result<(u64, JubJubScalar), Error> {
240        let R = self.stealth_address.R();
241        let shared_secret = dhke(vk.a(), R);
242
243        let salt = self.stealth_address.to_bytes();
244
245        let dec_plaintext: [u8; PLAINTEXT_SIZE] =
246            aes::decrypt(&shared_secret, &salt, &self.value_enc)?;
247
248        let value = u64::from_slice(&dec_plaintext[..u64::SIZE])?;
249
250        // Converts the BLS Scalar into a JubJub Scalar.
251        // If the `vk` is wrong it might fails since the resulting BLS Scalar
252        // might not fit into a JubJub Scalar.
253        let value_blinder =
254            match JubJubScalar::from_slice(&dec_plaintext[u64::SIZE..])?.into()
255            {
256                Some(scalar) => scalar,
257                None => return Err(Error::InvalidData),
258            };
259
260        Ok((value, value_blinder))
261    }
262
263    /// Create a unique nullifier for the note
264    ///
265    /// This nullifier is represeted as `H(note_sk ยท G', pos)`
266    pub fn gen_nullifier(&self, sk: &SecretKey) -> BlsScalar {
267        let note_sk = sk.gen_note_sk(&self.stealth_address);
268        let pk_prime = GENERATOR_NUMS_EXTENDED * note_sk.as_ref();
269        let pk_prime = pk_prime.to_hash_inputs();
270
271        let pos = BlsScalar::from(self.pos);
272
273        Hash::digest(Domain::Other, &[pk_prime[0], pk_prime[1], pos])[0]
274    }
275
276    /// Return the internal representation of scalars to be hashed
277    pub fn hash_inputs(&self) -> [BlsScalar; 6] {
278        let note_pk =
279            self.stealth_address().note_pk().as_ref().to_hash_inputs();
280
281        [
282            BlsScalar::from(self.note_type as u64),
283            self.value_commitment.get_u(),
284            self.value_commitment.get_v(),
285            note_pk[0],
286            note_pk[1],
287            BlsScalar::from(self.pos),
288        ]
289    }
290
291    /// Return a hash represented by `H(note_type, value_commitment,
292    /// H(StealthAddress), pos, encrypted_data)
293    pub fn hash(&self) -> BlsScalar {
294        Hash::digest(Domain::Other, &self.hash_inputs())[0]
295    }
296
297    /// Return the type of the note
298    pub const fn note_type(&self) -> NoteType {
299        self.note_type
300    }
301
302    /// Return the position of the note on the tree.
303    pub const fn pos(&self) -> &u64 {
304        &self.pos
305    }
306
307    /// Returns the the stealth address associated with the note.
308    pub const fn stealth_address(&self) -> &StealthAddress {
309        &self.stealth_address
310    }
311
312    /// Set the position of the note on the tree.
313    /// This, naturally, won't reflect immediatelly on the data storage
314    pub fn set_pos(&mut self, pos: u64) {
315        self.pos = pos;
316    }
317
318    /// Return the value commitment `H(value, value_blinder)`
319    pub const fn value_commitment(&self) -> &JubJubAffine {
320        &self.value_commitment
321    }
322
323    /// Returns the cipher of the encrypted data
324    pub const fn value_enc(&self) -> &[u8; VALUE_ENC_SIZE] {
325        &self.value_enc
326    }
327
328    /// Returns elgamal encryption of the sender's [`PublicKey`] encrypted using
329    /// the [`StealthAddress::note_pk`] so only the receiver of the [`Note`]
330    /// can decrypt.
331    pub const fn sender(&self) -> &Sender {
332        &self.sender
333    }
334
335    /// Attempt to decrypt the note value provided a [`ViewKey`]. Always
336    /// succeeds for transparent notes, might fails or return random values for
337    /// obfuscated notes if the provided view key is wrong.
338    pub fn value(&self, vk: Option<&ViewKey>) -> Result<u64, Error> {
339        match (self.note_type, vk) {
340            (NoteType::Transparent, _) => {
341                let value =
342                    u64::from_slice(&self.value_enc[..u64::SIZE]).unwrap();
343                Ok(value)
344            }
345            (NoteType::Obfuscated, Some(vk)) => {
346                self.decrypt_value(vk).map(|(value, _)| value)
347            }
348            _ => Err(Error::MissingViewKey),
349        }
350    }
351
352    /// Decrypt the blinding factor with the provided [`ViewKey`]
353    ///
354    /// If the decrypt fails, a random value is returned
355    pub fn value_blinder(
356        &self,
357        vk: Option<&ViewKey>,
358    ) -> Result<JubJubScalar, Error> {
359        match (self.note_type, vk) {
360            (NoteType::Transparent, _) => Ok(TRANSPARENT_BLINDER),
361            (NoteType::Obfuscated, Some(vk)) => self
362                .decrypt_value(vk)
363                .map(|(_, value_blinder)| value_blinder),
364            _ => Err(Error::MissingViewKey),
365        }
366    }
367}
368
369const SIZE: usize = 1
370    + JubJubAffine::SIZE
371    + StealthAddress::SIZE
372    + u64::SIZE
373    + VALUE_ENC_SIZE
374    + Sender::SIZE;
375
376impl Serializable<SIZE> for Note {
377    type Error = BytesError;
378
379    /// Converts a Note into a byte representation
380    fn to_bytes(&self) -> [u8; Self::SIZE] {
381        let mut buf = [0u8; Self::SIZE];
382
383        buf[0] = self.note_type as u8;
384
385        let mut start = 1;
386
387        buf[start..start + JubJubAffine::SIZE]
388            .copy_from_slice(&self.value_commitment.to_bytes());
389        start += JubJubAffine::SIZE;
390
391        buf[start..start + StealthAddress::SIZE]
392            .copy_from_slice(&self.stealth_address.to_bytes());
393        start += StealthAddress::SIZE;
394
395        buf[start..start + u64::SIZE].copy_from_slice(&self.pos.to_le_bytes());
396        start += u64::SIZE;
397
398        buf[start..start + VALUE_ENC_SIZE].copy_from_slice(&self.value_enc);
399        start += VALUE_ENC_SIZE;
400
401        buf[start..start + Sender::SIZE]
402            .copy_from_slice(&self.sender.to_bytes());
403
404        buf
405    }
406
407    /// Attempts to convert a byte representation of a note into a `Note`,
408    /// failing if the input is invalid
409    fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result<Self, Self::Error> {
410        let note_type =
411            bytes[0].try_into().map_err(|_| BytesError::InvalidData)?;
412
413        let mut buf = &bytes[1..];
414
415        let value_commitment = JubJubAffine::from_reader(&mut buf)?;
416
417        let stealth_address = StealthAddress::from_reader(&mut buf)?;
418
419        let pos = u64::from_reader(&mut buf)?;
420
421        let mut value_enc = [0u8; VALUE_ENC_SIZE];
422        value_enc.copy_from_slice(&buf[..VALUE_ENC_SIZE]);
423        buf = &buf[VALUE_ENC_SIZE..];
424
425        let sender = Sender::from_reader(&mut buf)?;
426
427        Ok(Note {
428            note_type,
429            value_commitment,
430            stealth_address,
431            pos,
432            value_enc,
433            sender,
434        })
435    }
436}
437
438/// The sender of the `Note`.
439/// This can be either the encrypted sender's [`PublicKey`], if the [`Note`] was
440/// created as an output note of a phoenix-transaction, or some contract-data if
441/// the [`Note`] was created in another way, e.g. by withdrawing from a
442/// contract.
443#[derive(Copy, Clone, Debug, PartialEq, Eq)]
444#[cfg_attr(
445    feature = "rkyv-impl",
446    derive(Archive, Serialize, Deserialize),
447    archive_attr(derive(bytecheck::CheckBytes))
448)]
449pub enum Sender {
450    /// The sender's [`PublicKey`], encrypted using the note_pk of the
451    /// stealth-address.
452    Encryption([(JubJubAffine, JubJubAffine); 2]),
453    /// Information to identify the origin of a `Note`, if it wasn't created as
454    /// a phoenix-transaction output-note.
455    ContractInfo([u8; 4 * JubJubAffine::SIZE]),
456}
457
458impl Sender {
459    /// Create a new [`Sender`] enum by encrypting the sender's [`PublicKey`] in
460    /// a way that only the receiver of the note can decrypt.
461    pub fn encrypt(
462        note_pk: &NotePublicKey,
463        sender_pk: &PublicKey,
464        blinder: &[JubJubScalar; 2],
465    ) -> Self {
466        let sender_enc_A =
467            elgamal::encrypt(note_pk.as_ref(), sender_pk.A(), &blinder[0]);
468
469        let sender_enc_B =
470            elgamal::encrypt(note_pk.as_ref(), sender_pk.B(), &blinder[1]);
471
472        let sender_enc_A: (JubJubAffine, JubJubAffine) =
473            (sender_enc_A.0.into(), sender_enc_A.1.into());
474        let sender_enc_B: (JubJubAffine, JubJubAffine) =
475            (sender_enc_B.0.into(), sender_enc_B.1.into());
476
477        Self::Encryption([sender_enc_A, sender_enc_B])
478    }
479
480    /// Decrypts the [`PublicKey`] of the sender of the [`Note`], using the
481    /// [`NoteSecretKey`] generated by the receiver's [`SecretKey`] and the
482    /// [`StealthAddress`] of the [`Note`].
483    ///
484    /// Note: Decryption with an *incorrect* [`NoteSecretKey`] will still yield
485    /// a [`PublicKey`], but in this case, the public-key will be a random one
486    /// that has nothing to do with the sender's [`PublicKey`].
487    ///
488    /// Returns an error if the sender is of type [`Sender::ContractInfo`].
489    pub fn decrypt(&self, note_sk: &NoteSecretKey) -> Result<PublicKey, Error> {
490        let sender_enc = match self {
491            Sender::Encryption(enc) => enc,
492            Sender::ContractInfo(_) => {
493                return Err(Error::InvalidEncryption);
494            }
495        };
496
497        let sender_enc_A = sender_enc[0];
498        let sender_enc_B = sender_enc[1];
499
500        let decrypt_A = elgamal::decrypt(
501            note_sk.as_ref(),
502            &(sender_enc_A.0.into(), sender_enc_A.1.into()),
503        );
504        let decrypt_B = elgamal::decrypt(
505            note_sk.as_ref(),
506            &(sender_enc_B.0.into(), sender_enc_B.1.into()),
507        );
508
509        Ok(PublicKey::new(decrypt_A, decrypt_B))
510    }
511}
512
513impl Serializable<{ 1 + 4 * JubJubAffine::SIZE }> for Sender {
514    type Error = BytesError;
515
516    /// Converts a Note into a byte representation
517    fn to_bytes(&self) -> [u8; Self::SIZE] {
518        let mut buf = [0u8; Self::SIZE];
519
520        match self {
521            Sender::Encryption(sender_enc) => {
522                buf[0] = 0;
523                let mut start = 1;
524
525                buf[start..start + JubJubAffine::SIZE]
526                    .copy_from_slice(&sender_enc[0].0.to_bytes());
527                start += JubJubAffine::SIZE;
528
529                buf[start..start + JubJubAffine::SIZE]
530                    .copy_from_slice(&sender_enc[0].1.to_bytes());
531                start += JubJubAffine::SIZE;
532
533                buf[start..start + JubJubAffine::SIZE]
534                    .copy_from_slice(&sender_enc[1].0.to_bytes());
535                start += JubJubAffine::SIZE;
536
537                buf[start..start + JubJubAffine::SIZE]
538                    .copy_from_slice(&sender_enc[1].1.to_bytes());
539            }
540            Sender::ContractInfo(contract_data) => {
541                buf[0] = 1;
542                buf[1..].copy_from_slice(&contract_data[..]);
543            }
544        }
545
546        buf
547    }
548
549    /// Attempts to convert a byte representation of a note into a `Note`,
550    /// failing if the input is invalid
551    fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result<Self, Self::Error> {
552        let sender = match bytes[0] {
553            0 => {
554                let mut buf = &bytes[1..];
555                let sender_enc_A_0 = JubJubAffine::from_reader(&mut buf)?;
556                let sender_enc_A_1 = JubJubAffine::from_reader(&mut buf)?;
557                let sender_enc_B_0 = JubJubAffine::from_reader(&mut buf)?;
558                let sender_enc_B_1 = JubJubAffine::from_reader(&mut buf)?;
559                Sender::Encryption([
560                    (sender_enc_A_0, sender_enc_A_1),
561                    (sender_enc_B_0, sender_enc_B_1),
562                ])
563            }
564            1 => {
565                let mut contract_data = [0u8; 4 * JubJubAffine::SIZE];
566                contract_data.copy_from_slice(&bytes[1..Self::SIZE]);
567                Sender::ContractInfo(contract_data)
568            }
569            _ => return Err(BytesError::InvalidData),
570        };
571
572        Ok(sender)
573    }
574}