Skip to main content

phoenix_circuits/
lib.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
7//! Phoenix's circuits and gadgets.
8
9#![allow(non_snake_case)]
10#![deny(missing_docs)]
11#![no_std]
12
13#[cfg(feature = "plonk")]
14mod circuit_impl;
15#[cfg(feature = "plonk")]
16mod sender_enc;
17
18use dusk_bls12_381::BlsScalar;
19use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
20use dusk_jubjub::{JubJubAffine, JubJubScalar};
21use jubjub_schnorr::{Signature as SchnorrSignature, SignatureDouble};
22use phoenix_core::{Note, OUTPUT_NOTES, PublicKey, SecretKey};
23use poseidon_merkle::{ARITY, Item, Opening, Tree};
24#[cfg(feature = "rkyv-impl")]
25use rkyv::{Archive, Deserialize, Serialize};
26
27extern crate alloc;
28use alloc::vec::Vec;
29
30/// Declaration of the transaction circuit calling the [`gadget`].
31#[derive(Debug, Clone, PartialEq)]
32#[cfg_attr(
33    feature = "rkyv-impl",
34    derive(Archive, Serialize, Deserialize),
35    archive_attr(derive(bytecheck::CheckBytes))
36)]
37pub struct TxCircuit<const H: usize, const I: usize> {
38    /// All information needed in relation to the transaction input-notes
39    pub input_notes_info: [InputNoteInfo<H>; I],
40    /// All information needed in relation to the transaction output-notes
41    pub output_notes_info: [OutputNoteInfo; OUTPUT_NOTES],
42    /// The hash of the transaction-payload
43    pub payload_hash: BlsScalar,
44    /// The root of the tree of notes corresponding to the input-note openings
45    pub root: BlsScalar,
46    /// The deposit of the transaction, is zero if there is no deposit
47    pub deposit: u64,
48    /// The maximum fee that the transaction may spend
49    pub max_fee: u64,
50    /// The public key of the sender used for the sender-encryption
51    pub sender_pk: PublicKey,
52    /// The signature of the payload-hash signed with sk.a and sk.b
53    pub signatures: (SchnorrSignature, SchnorrSignature),
54}
55
56impl<const H: usize, const I: usize> TxCircuit<H, I> {
57    /// The size of a `TxCircuit`.
58    pub const SIZE: usize = I * InputNoteInfo::<H>::SIZE
59        + OUTPUT_NOTES * OutputNoteInfo::SIZE
60        + 2 * BlsScalar::SIZE
61        + 2 * u64::SIZE
62        + PublicKey::SIZE
63        + 2 * SchnorrSignature::SIZE;
64
65    /// Serialize a [`TxCircuit`] to a vector of bytes.
66    // Once the new implementation of the `Serializable` trait becomes
67    // available, we will want that instead, but for the time being we use
68    // this implementation.
69    pub fn to_var_bytes(&self) -> Vec<u8> {
70        let mut bytes = Vec::with_capacity(Self::SIZE);
71
72        for info in self.input_notes_info.iter() {
73            bytes.extend(info.to_var_bytes());
74        }
75        for info in self.output_notes_info.iter() {
76            bytes.extend(info.to_bytes());
77        }
78        bytes.extend(self.payload_hash.to_bytes());
79        bytes.extend(self.root.to_bytes());
80        bytes.extend(self.deposit.to_bytes());
81        bytes.extend(self.max_fee.to_bytes());
82        bytes.extend(self.sender_pk.to_bytes());
83        bytes.extend(self.signatures.0.to_bytes());
84        bytes.extend(self.signatures.1.to_bytes());
85
86        bytes
87    }
88
89    /// Deserialize a [`TxCircuit`] from a slice of bytes.
90    ///
91    /// # Errors
92    ///
93    /// Will return [`dusk_bytes::Error`] in case of a deserialization error.
94    // Once the new implementation of the `Serializable` trait becomes
95    // available, we will want that instead, but for the time being we use
96    // this implementation.
97    pub fn from_slice(bytes: &[u8]) -> Result<Self, BytesError> {
98        if bytes.len() < Self::SIZE {
99            return Err(BytesError::BadLength {
100                found: bytes.len(),
101                expected: Self::SIZE,
102            });
103        }
104
105        let mut input_notes_info = Vec::new();
106        for i in 0..I {
107            let start = i * InputNoteInfo::<H>::SIZE;
108            input_notes_info.push(InputNoteInfo::from_slice(&bytes[start..])?);
109        }
110
111        let mut reader = &bytes[I * InputNoteInfo::<H>::SIZE..];
112
113        let output_notes_info = [
114            OutputNoteInfo::from_reader(&mut reader)?,
115            OutputNoteInfo::from_reader(&mut reader)?,
116        ];
117        let payload_hash = BlsScalar::from_reader(&mut reader)?;
118        let root = BlsScalar::from_reader(&mut reader)?;
119        let deposit = u64::from_reader(&mut reader)?;
120        let max_fee = u64::from_reader(&mut reader)?;
121        let sender_pk = PublicKey::from_reader(&mut reader)?;
122        let signature_0 = SchnorrSignature::from_reader(&mut reader)?;
123        let signature_1 = SchnorrSignature::from_reader(&mut reader)?;
124
125        Ok(Self {
126            input_notes_info: input_notes_info
127                .try_into()
128                .expect("The vector has exactly I elements"),
129            output_notes_info,
130            payload_hash,
131            root,
132            deposit,
133            max_fee,
134            sender_pk,
135            signatures: (signature_0, signature_1),
136        })
137    }
138}
139
140impl<const H: usize, const I: usize> Default for TxCircuit<H, I> {
141    fn default() -> Self {
142        let sk =
143            SecretKey::new(JubJubScalar::default(), JubJubScalar::default());
144
145        let mut tree = Tree::<(), H>::new();
146        let payload_hash = BlsScalar::default();
147
148        let mut input_notes_info = Vec::new();
149        let note = Note::empty();
150        let item = Item {
151            hash: note.hash(),
152            data: (),
153        };
154        tree.insert(*note.pos(), item);
155
156        for _ in 0..I {
157            let merkle_opening = tree.opening(*note.pos()).expect("Tree read.");
158            input_notes_info.push(InputNoteInfo {
159                merkle_opening,
160                note: note.clone(),
161                note_pk_p: JubJubAffine::default(),
162                value: 0u64,
163                value_blinder: JubJubScalar::default(),
164                nullifier: BlsScalar::default(),
165                signature: SignatureDouble::default(),
166            });
167        }
168
169        let output_note_info_0 = OutputNoteInfo {
170            value: 0,
171            value_commitment: JubJubAffine::default(),
172            value_blinder: JubJubScalar::default(),
173            note_pk: JubJubAffine::default(),
174            sender_enc: [(JubJubAffine::default(), JubJubAffine::default()); 2],
175            sender_blinder: [JubJubScalar::default(), JubJubScalar::default()],
176        };
177        let output_note_info_1 = output_note_info_0.clone();
178
179        let output_notes_info = [output_note_info_0, output_note_info_1];
180
181        let root = BlsScalar::default();
182        let deposit = u64::default();
183        let max_fee = u64::default();
184
185        let signatures =
186            (SchnorrSignature::default(), SchnorrSignature::default());
187
188        Self {
189            input_notes_info: input_notes_info.try_into().unwrap(),
190            output_notes_info,
191            payload_hash,
192            root,
193            deposit,
194            max_fee,
195            sender_pk: PublicKey::from(&sk),
196            signatures,
197        }
198    }
199}
200
201/// Struct holding all information needed by the transfer circuit regarding the
202/// transaction input-notes.
203#[derive(Debug, Clone, PartialEq)]
204#[cfg_attr(
205    feature = "rkyv-impl",
206    derive(Archive, Serialize, Deserialize),
207    archive_attr(derive(bytecheck::CheckBytes))
208)]
209pub struct InputNoteInfo<const H: usize> {
210    /// The merkle opening for the note
211    pub merkle_opening: Opening<(), H>,
212    /// The input note
213    pub note: Note,
214    /// The note-public-key prime
215    pub note_pk_p: JubJubAffine,
216    /// The value associated to the note
217    pub value: u64,
218    /// The value blinder used to obfuscate the value
219    pub value_blinder: JubJubScalar,
220    /// The nullifier used to spend the note
221    pub nullifier: BlsScalar,
222    /// The signature of the payload-hash, signed with the note-sk
223    pub signature: SignatureDouble,
224}
225
226impl<const H: usize> InputNoteInfo<H> {
227    /// The size of an `InputNoteInfo`
228    pub const SIZE: usize = (1 + H * ARITY) * Item::SIZE
229        + H * (u32::BITS as usize / 8)
230        + Note::SIZE
231        + JubJubAffine::SIZE
232        + u64::SIZE
233        + JubJubScalar::SIZE
234        + BlsScalar::SIZE
235        + SignatureDouble::SIZE;
236
237    /// Serialize an [`InputNoteInfo`] to a vector of bytes.
238    // Once the new implementation of the `Serializable` trait becomes
239    // available, we will want that instead, but for the time being we use
240    // this implementation.
241    pub fn to_var_bytes(&self) -> Vec<u8> {
242        let mut bytes = Vec::with_capacity(Self::SIZE);
243
244        bytes.extend(self.merkle_opening.to_var_bytes());
245        bytes.extend(self.note.to_bytes());
246        bytes.extend(self.note_pk_p.to_bytes());
247        bytes.extend(self.value.to_bytes());
248        bytes.extend(self.value_blinder.to_bytes());
249        bytes.extend(self.nullifier.to_bytes());
250        bytes.extend(self.signature.to_bytes());
251
252        bytes
253    }
254
255    /// Deserialize an [`InputNoteInfo`] from a slice of bytes.
256    ///
257    /// # Errors
258    ///
259    /// Will return [`dusk_bytes::Error`] in case of a deserialization error.
260    // Once the new implementation of the `Serializable` trait becomes
261    // available, we will want that instead, but for the time being we use
262    // this implementation.
263    pub fn from_slice(bytes: &[u8]) -> Result<Self, BytesError> {
264        if bytes.len() < Self::SIZE {
265            return Err(BytesError::BadLength {
266                found: bytes.len(),
267                expected: Self::SIZE,
268            });
269        }
270
271        let merkle_opening_size =
272            (1 + H * ARITY) * Item::SIZE + H * (u32::BITS as usize / 8);
273        let merkle_opening =
274            Opening::<(), H>::from_slice(&bytes[..merkle_opening_size])?;
275
276        let mut buf = &bytes[merkle_opening_size..];
277        let note = Note::from_reader(&mut buf)?;
278        let note_pk_p = JubJubAffine::from_reader(&mut buf)?;
279        let value = u64::from_reader(&mut buf)?;
280        let value_blinder = JubJubScalar::from_reader(&mut buf)?;
281        let nullifier = BlsScalar::from_reader(&mut buf)?;
282        let signature = SignatureDouble::from_reader(&mut buf)?;
283
284        Ok(Self {
285            merkle_opening,
286            note,
287            note_pk_p,
288            value,
289            value_blinder,
290            nullifier,
291            signature,
292        })
293    }
294}
295
296/// Struct holding all information needed by the transfer circuit regarding the
297/// transaction output-notes.
298#[derive(Debug, Clone, PartialEq)]
299#[cfg_attr(
300    feature = "rkyv-impl",
301    derive(Archive, Serialize, Deserialize),
302    archive_attr(derive(bytecheck::CheckBytes))
303)]
304pub struct OutputNoteInfo {
305    /// The value of the note
306    pub value: u64,
307    /// The value-commitment of the note
308    pub value_commitment: JubJubAffine,
309    /// The blinder used to calculate the value commitment
310    pub value_blinder: JubJubScalar,
311    /// The public key of the note
312    pub note_pk: JubJubAffine,
313    /// The encrypted sender information of the note
314    pub sender_enc: [(JubJubAffine, JubJubAffine); 2],
315    /// The blinder used to encrypt the sender
316    pub sender_blinder: [JubJubScalar; 2],
317}
318
319const OUTPUT_NOTE_INFO_SIZE: usize = u64::SIZE
320    + JubJubAffine::SIZE
321    + JubJubScalar::SIZE
322    + JubJubAffine::SIZE
323    + 4 * JubJubAffine::SIZE
324    + 2 * JubJubScalar::SIZE;
325
326impl Serializable<OUTPUT_NOTE_INFO_SIZE> for OutputNoteInfo {
327    type Error = BytesError;
328
329    fn to_bytes(&self) -> [u8; Self::SIZE] {
330        let mut bytes = [0u8; Self::SIZE];
331        let mut offset = 0;
332
333        bytes[..u64::SIZE].copy_from_slice(&self.value.to_bytes());
334        offset += u64::SIZE;
335        bytes[offset..offset + JubJubAffine::SIZE]
336            .copy_from_slice(&self.value_commitment.to_bytes());
337        offset += JubJubAffine::SIZE;
338        bytes[offset..offset + JubJubScalar::SIZE]
339            .copy_from_slice(&self.value_blinder.to_bytes());
340        offset += JubJubScalar::SIZE;
341        bytes[offset..offset + JubJubAffine::SIZE]
342            .copy_from_slice(&self.note_pk.to_bytes());
343        offset += JubJubAffine::SIZE;
344        bytes[offset..offset + JubJubAffine::SIZE]
345            .copy_from_slice(&self.sender_enc[0].0.to_bytes());
346        offset += JubJubAffine::SIZE;
347        bytes[offset..offset + JubJubAffine::SIZE]
348            .copy_from_slice(&self.sender_enc[0].1.to_bytes());
349        offset += JubJubAffine::SIZE;
350        bytes[offset..offset + JubJubAffine::SIZE]
351            .copy_from_slice(&self.sender_enc[1].0.to_bytes());
352        offset += JubJubAffine::SIZE;
353        bytes[offset..offset + JubJubAffine::SIZE]
354            .copy_from_slice(&self.sender_enc[1].1.to_bytes());
355        offset += JubJubAffine::SIZE;
356        bytes[offset..offset + JubJubScalar::SIZE]
357            .copy_from_slice(&self.sender_blinder[0].to_bytes());
358        offset += JubJubScalar::SIZE;
359        bytes[offset..offset + JubJubScalar::SIZE]
360            .copy_from_slice(&self.sender_blinder[1].to_bytes());
361
362        bytes
363    }
364
365    fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result<Self, Self::Error> {
366        let mut reader = &bytes[..];
367
368        let value = u64::from_reader(&mut reader)?;
369        let value_commitment = JubJubAffine::from_reader(&mut reader)?;
370        let value_blinder = JubJubScalar::from_reader(&mut reader)?;
371        let note_pk = JubJubAffine::from_reader(&mut reader)?;
372        let sender_enc_0_0 = JubJubAffine::from_reader(&mut reader)?;
373        let sender_enc_0_1 = JubJubAffine::from_reader(&mut reader)?;
374        let sender_enc_1_0 = JubJubAffine::from_reader(&mut reader)?;
375        let sender_enc_1_1 = JubJubAffine::from_reader(&mut reader)?;
376        let sender_blinder_0 = JubJubScalar::from_reader(&mut reader)?;
377        let sender_blinder_1 = JubJubScalar::from_reader(&mut reader)?;
378
379        Ok(Self {
380            value,
381            value_commitment,
382            value_blinder,
383            note_pk,
384            sender_enc: [
385                (sender_enc_0_0, sender_enc_0_1),
386                (sender_enc_1_0, sender_enc_1_1),
387            ],
388            sender_blinder: [sender_blinder_0, sender_blinder_1],
389        })
390    }
391}