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