Skip to main content

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