liminal_ark_relations/shielder/
note_var.rs

1use ark_r1cs_std::{
2    alloc::{AllocVar, AllocationMode},
3    eq::EqGadget,
4};
5use ark_relations::{
6    ns,
7    r1cs::{ConstraintSystemRef, SynthesisError},
8};
9use paste::paste;
10
11use crate::{
12    environment::{CircuitField, FpVar},
13    shielder::{
14        token_amount_var::TokenAmountVar,
15        types::{
16            BackendNote, BackendNullifier, BackendTokenAmount, BackendTokenId, BackendTrapdoor,
17        },
18    },
19};
20
21#[derive(Clone, Debug)]
22pub struct NoteVar {
23    pub token_id: FpVar,
24    pub token_amount: TokenAmountVar,
25    pub trapdoor: FpVar,
26    pub nullifier: FpVar,
27    pub note: FpVar,
28}
29
30#[derive(Clone, Debug)]
31pub struct NoteVarBuilder<
32    const TOKEN_ID_SET: bool,
33    const TOKEN_AMOUNT_SET: bool,
34    const TRAPDOOR_SET: bool,
35    const NULLIFIER_SET: bool,
36    const NOTE_SET: bool,
37> {
38    token_id: Option<FpVar>,
39    token_amount: Option<TokenAmountVar>,
40    trapdoor: Option<FpVar>,
41    nullifier: Option<FpVar>,
42    note: Option<FpVar>,
43    cs: ConstraintSystemRef<CircuitField>,
44}
45
46impl NoteVarBuilder<false, false, false, false, false> {
47    pub fn new(cs: ConstraintSystemRef<CircuitField>) -> Self {
48        NoteVarBuilder {
49            token_id: None,
50            token_amount: None,
51            trapdoor: None,
52            nullifier: None,
53            note: None,
54            cs,
55        }
56    }
57}
58
59type Result<T> = core::result::Result<T, SynthesisError>;
60
61macro_rules! impl_with_plain_arg {
62    ($item: ident, $item_type: ty, $target_type: ty, $target_item_type: ty) => {
63        paste! {
64            pub fn [<with_ $item>] (self, $item: Result<&$item_type>, mode: AllocationMode) -> Result<$target_type> {
65                let $item = $target_item_type::new_variable(ns!(self.cs, stringify!($item)), || $item, mode)?;
66                Ok(self. [<with_ $item _var>]($item))
67            }
68        }
69    };
70}
71
72macro_rules! impl_with_var_arg {
73    ($item: ident, $target_type: ty, $target_item_type: ty) => {
74        paste! {
75            pub fn [<with_ $item _var>] (self, $item: $target_item_type) -> $target_type {
76                let mut note: $target_type = NoteVarBuilder {
77                    token_id: self.token_id,
78                    token_amount: self.token_amount,
79                    trapdoor: self.trapdoor,
80                    nullifier: self.nullifier,
81                    note: self.note,
82                    cs: self.cs,
83                };
84                note.$item = Some($item);
85                note
86            }
87        }
88    };
89}
90
91macro_rules! impl_builder {
92    ($in_type: ty, $out_type: ty, $item: ident, $item_type: ty) => {
93        impl_builder!($in_type, $out_type, $item, $item_type, FpVar);
94    };
95    ($in_type: ty, $out_type: ty, $item: ident, $item_type: ty, $target_item_type: ty) => {
96        impl<const _1: bool, const _2: bool, const _3: bool, const _4: bool> $in_type {
97            impl_with_plain_arg!($item, $item_type, $out_type, $target_item_type);
98            impl_with_var_arg!($item, $out_type, $target_item_type);
99        }
100    };
101}
102
103impl_builder!(
104    NoteVarBuilder<false, _1, _2, _3, _4>,
105    NoteVarBuilder<true, _1, _2, _3, _4>,
106    token_id, BackendTokenId
107);
108impl_builder!(
109    NoteVarBuilder<_1, false, _2, _3, _4>,
110    NoteVarBuilder<_1, true, _2, _3, _4>,
111    token_amount, BackendTokenAmount, TokenAmountVar
112);
113impl_builder!(
114    NoteVarBuilder<_1, _2, false, _3, _4>,
115    NoteVarBuilder<_1, _2, true, _3, _4>,
116    trapdoor, BackendTrapdoor
117);
118impl_builder!(
119    NoteVarBuilder<_1, _2, _3, false, _4>,
120    NoteVarBuilder<_1, _2, _3, true, _4>,
121    nullifier, BackendNullifier
122);
123impl_builder!(
124    NoteVarBuilder<_1, _2, _3, _4, false>,
125    NoteVarBuilder<_1, _2, _3, _4, true>,
126    note, BackendNote
127);
128
129impl NoteVarBuilder<true, true, true, true, true> {
130    /// Verify that `note` is indeed the result of hashing `(token_id, token_amount, trapdoor,
131    /// nullifier)`. If so, return `NoteVar` holding all components.
132    pub fn build(self) -> Result<NoteVar> {
133        let note = NoteVar {
134            token_id: self.token_id.unwrap(),
135            token_amount: self.token_amount.unwrap(),
136            trapdoor: self.trapdoor.unwrap(),
137            nullifier: self.nullifier.unwrap(),
138            note: self.note.unwrap(),
139        };
140
141        let hash = liminal_ark_poseidon::circuit::four_to_one_hash(
142            self.cs,
143            [
144                note.token_id.clone(),
145                note.token_amount.clone().into(),
146                note.trapdoor.clone(),
147                note.nullifier.clone(),
148            ],
149        )?;
150
151        hash.enforce_equal(&note.note)?;
152
153        Ok(note)
154    }
155}