penumbra_sdk_shielded_pool/note/
r1cs.rs

1use ark_ff::ToConstraintField;
2use ark_r1cs_std::prelude::*;
3use ark_relations::r1cs::SynthesisError;
4use decaf377::{
5    r1cs::{ElementVar, FqVar},
6    Fq,
7};
8use penumbra_sdk_asset::ValueVar;
9use penumbra_sdk_keys::address::AddressVar;
10use penumbra_sdk_tct::r1cs::StateCommitmentVar;
11
12use crate::Note;
13
14use super::NOTECOMMIT_DOMAIN_SEP;
15
16pub struct NoteVar {
17    pub value: ValueVar,
18    pub note_blinding: FqVar,
19    pub address: AddressVar,
20}
21
22impl NoteVar {
23    pub fn amount(&self) -> FqVar {
24        self.value.amount()
25    }
26
27    pub fn value(&self) -> ValueVar {
28        self.value.clone()
29    }
30
31    #[allow(dead_code)]
32    pub fn asset_id(&self) -> FqVar {
33        self.value.asset_id()
34    }
35
36    #[allow(dead_code)]
37    pub fn note_blinding(&self) -> FqVar {
38        self.note_blinding.clone()
39    }
40
41    pub fn diversified_generator(&self) -> ElementVar {
42        self.address.diversified_generator.clone()
43    }
44
45    pub fn transmission_key(&self) -> ElementVar {
46        self.address.transmission_key.clone()
47    }
48
49    #[allow(dead_code)]
50    pub fn clue_key(&self) -> FqVar {
51        self.address.clue_key.clone()
52    }
53}
54
55impl AllocVar<Note, Fq> for NoteVar {
56    fn new_variable<T: std::borrow::Borrow<Note>>(
57        cs: impl Into<ark_relations::r1cs::Namespace<Fq>>,
58        f: impl FnOnce() -> Result<T, SynthesisError>,
59        mode: ark_r1cs_std::prelude::AllocationMode,
60    ) -> Result<Self, SynthesisError> {
61        // TODO: figure out how to use namespaces
62        let ns = cs.into();
63        let cs = ns.cs();
64        let note1 = f()?;
65        let note: &Note = note1.borrow();
66        let note_blinding = FqVar::new_variable(cs.clone(), || Ok(note.note_blinding()), mode)?;
67        let value = ValueVar::new_variable(cs.clone(), || Ok(note.value()), mode)?;
68        let address = AddressVar::new_variable(cs, || Ok(note.address()), mode)?;
69
70        Ok(Self {
71            note_blinding,
72            value,
73            address,
74        })
75    }
76}
77
78impl ToConstraintField<Fq> for Note {
79    fn to_field_elements(&self) -> Option<Vec<Fq>> {
80        let mut elements = Vec::new();
81        let note_blinding = self.note_blinding();
82        elements.extend([note_blinding]);
83        elements.extend(self.value().to_field_elements()?);
84        elements.extend(self.address().to_field_elements()?);
85        Some(elements)
86    }
87}
88
89// We do not implement `R1CSVar` for `NoteVar` since the associated type
90// should be `Note` which we cannot construct from the R1CS variable
91// since we do not have the rseed in-circuit.
92
93impl NoteVar {
94    pub fn commit(&self) -> Result<StateCommitmentVar, SynthesisError> {
95        let cs = self.amount().cs();
96        let domain_sep = FqVar::new_constant(cs.clone(), *NOTECOMMIT_DOMAIN_SEP)?;
97        let compressed_g_d = self.address.diversified_generator().compress_to_field()?;
98
99        let commitment = poseidon377::r1cs::hash_6(
100            cs,
101            &domain_sep,
102            (
103                self.note_blinding.clone(),
104                self.value.amount(),
105                self.value.asset_id(),
106                compressed_g_d,
107                self.address.transmission_key().compress_to_field()?,
108                self.address.clue_key(),
109            ),
110        )?;
111
112        Ok(StateCommitmentVar { inner: commitment })
113    }
114}