Skip to main content

orchard/
action.rs

1use memuse::DynamicUsage;
2
3use crate::{
4    note::{ExtractedNoteCommitment, Nullifier, Rho, TransmittedNoteCiphertext},
5    primitives::redpallas::{self, SpendAuth},
6    value::ValueCommitment,
7};
8
9/// An action applied to the global ledger.
10///
11/// This both creates a note (adding a commitment to the global ledger), and consumes
12/// some note created prior to this action (adding a nullifier to the global ledger).
13#[derive(Debug, Clone)]
14pub struct Action<A> {
15    /// The nullifier of the note being spent.
16    nf: Nullifier,
17    /// The randomized verification key for the note being spent.
18    rk: redpallas::VerificationKey<SpendAuth>,
19    /// A commitment to the new note being created.
20    cmx: ExtractedNoteCommitment,
21    /// The transmitted note ciphertext.
22    encrypted_note: TransmittedNoteCiphertext,
23    /// A commitment to the net value created or consumed by this action.
24    cv_net: ValueCommitment,
25    /// The authorization for this action.
26    authorization: A,
27}
28
29impl<T> Action<T> {
30    /// Constructs an `Action` from its constituent parts.
31    pub fn from_parts(
32        nf: Nullifier,
33        rk: redpallas::VerificationKey<SpendAuth>,
34        cmx: ExtractedNoteCommitment,
35        encrypted_note: TransmittedNoteCiphertext,
36        cv_net: ValueCommitment,
37        authorization: T,
38    ) -> Self {
39        Action {
40            nf,
41            rk,
42            cmx,
43            encrypted_note,
44            cv_net,
45            authorization,
46        }
47    }
48
49    /// Returns the nullifier of the note being spent.
50    pub fn nullifier(&self) -> &Nullifier {
51        &self.nf
52    }
53
54    /// Returns the randomized verification key for the note being spent.
55    pub fn rk(&self) -> &redpallas::VerificationKey<SpendAuth> {
56        &self.rk
57    }
58
59    /// Returns the commitment to the new note being created.
60    pub fn cmx(&self) -> &ExtractedNoteCommitment {
61        &self.cmx
62    }
63
64    /// Returns the encrypted note ciphertext.
65    pub fn encrypted_note(&self) -> &TransmittedNoteCiphertext {
66        &self.encrypted_note
67    }
68
69    /// Obtains the [`Rho`] value that was used to construct the new note being created.
70    pub fn rho(&self) -> Rho {
71        Rho::from_nf_old(self.nf)
72    }
73
74    /// Returns the commitment to the net value created or consumed by this action.
75    pub fn cv_net(&self) -> &ValueCommitment {
76        &self.cv_net
77    }
78
79    /// Returns the authorization for this action.
80    pub fn authorization(&self) -> &T {
81        &self.authorization
82    }
83
84    /// Transitions this action from one authorization state to another.
85    pub fn map<U>(self, step: impl FnOnce(T) -> U) -> Action<U> {
86        Action {
87            nf: self.nf,
88            rk: self.rk,
89            cmx: self.cmx,
90            encrypted_note: self.encrypted_note,
91            cv_net: self.cv_net,
92            authorization: step(self.authorization),
93        }
94    }
95
96    /// Transitions this action from one authorization state to another.
97    pub fn try_map<U, E>(self, step: impl FnOnce(T) -> Result<U, E>) -> Result<Action<U>, E> {
98        Ok(Action {
99            nf: self.nf,
100            rk: self.rk,
101            cmx: self.cmx,
102            encrypted_note: self.encrypted_note,
103            cv_net: self.cv_net,
104            authorization: step(self.authorization)?,
105        })
106    }
107}
108
109impl DynamicUsage for Action<redpallas::Signature<SpendAuth>> {
110    #[inline(always)]
111    fn dynamic_usage(&self) -> usize {
112        0
113    }
114
115    #[inline(always)]
116    fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
117        (0, Some(0))
118    }
119}
120
121/// Generators for property testing.
122#[cfg(any(test, feature = "test-dependencies"))]
123#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
124pub(crate) mod testing {
125    use rand::{rngs::StdRng, SeedableRng};
126    use reddsa::orchard::SpendAuth;
127
128    use proptest::prelude::*;
129
130    use crate::{
131        note::{
132            commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
133            testing::arb_note, TransmittedNoteCiphertext,
134        },
135        primitives::redpallas::{
136            self,
137            testing::{arb_spendauth_signing_key, arb_spendauth_verification_key},
138        },
139        value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
140    };
141
142    use super::Action;
143
144    prop_compose! {
145        /// Generate an action without authorization data.
146        pub fn arb_unauthorized_action(spend_value: NoteValue, output_value: NoteValue)(
147            nf in arb_nullifier(),
148            rk in arb_spendauth_verification_key(),
149            note in arb_note(output_value),
150        ) -> Action<()> {
151            let cmx = ExtractedNoteCommitment::from(note.commitment());
152            let cv_net = ValueCommitment::derive(
153                spend_value - output_value,
154                ValueCommitTrapdoor::zero()
155            );
156            // FIXME: make a real one from the note.
157            let encrypted_note = TransmittedNoteCiphertext {
158                epk_bytes: [0u8; 32],
159                enc_ciphertext: [0u8; 580],
160                out_ciphertext: [0u8; 80]
161            };
162            Action {
163                nf,
164                rk,
165                cmx,
166                encrypted_note,
167                cv_net,
168                authorization: ()
169            }
170        }
171    }
172
173    prop_compose! {
174        /// Generate an action with invalid (random) authorization data.
175        pub fn arb_action(spend_value: NoteValue, output_value: NoteValue)(
176            nf in arb_nullifier(),
177            sk in arb_spendauth_signing_key(),
178            note in arb_note(output_value),
179            rng_seed in prop::array::uniform32(prop::num::u8::ANY),
180            fake_sighash in prop::array::uniform32(prop::num::u8::ANY),
181        ) -> Action<redpallas::Signature<SpendAuth>> {
182            let cmx = ExtractedNoteCommitment::from(note.commitment());
183            let cv_net = ValueCommitment::derive(
184                spend_value - output_value,
185                ValueCommitTrapdoor::zero()
186            );
187
188            // FIXME: make a real one from the note.
189            let encrypted_note = TransmittedNoteCiphertext {
190                epk_bytes: [0u8; 32],
191                enc_ciphertext: [0u8; 580],
192                out_ciphertext: [0u8; 80]
193            };
194
195            let rng = StdRng::from_seed(rng_seed);
196
197            Action {
198                nf,
199                rk: redpallas::VerificationKey::from(&sk),
200                cmx,
201                encrypted_note,
202                cv_net,
203                authorization: sk.sign(rng, &fake_sighash),
204            }
205        }
206    }
207}