zebra_chain/orchard/shielded_data.rs
1//! Orchard shielded data for `V5` `Transaction`s.
2
3use std::{
4 cmp::{Eq, PartialEq},
5 fmt::{self, Debug},
6 io,
7};
8
9use byteorder::{ReadBytesExt, WriteBytesExt};
10use halo2::pasta::pallas;
11use reddsa::{orchard::Binding, orchard::SpendAuth, Signature};
12
13use crate::{
14 amount::{Amount, NegativeAllowed},
15 block::MAX_BLOCK_BYTES,
16 orchard::{tree, Action, Nullifier, ValueCommitment},
17 primitives::Halo2Proof,
18 serialization::{
19 AtLeastOne, SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashSerialize,
20 },
21};
22
23/// Returns the canonical size in bytes of an Orchard proof for `num_actions` actions.
24///
25/// An Orchard proof is a Halo2 proof whose length is exactly linear in the number of
26/// actions (circuit instances): 4992 bytes for 1 action and 7264 bytes for 2 actions,
27/// i.e. a fixed base plus 2272 bytes per action. The exact constants are owned by the
28/// `orchard` crate, which derives them from the action circuit's `halo2_proofs`
29/// `CircuitCost` and cross-checks them in its circuit tests, so we delegate to
30/// [`orchard::Proof::expected_proof_size`] rather than re-deriving them here. The
31/// `expected_proof_size_known_values` guard test cross-checks the returned values.
32pub(crate) fn expected_proof_size(num_actions: usize) -> usize {
33 orchard::Proof::expected_proof_size(num_actions)
34}
35
36/// A bundle of [`Action`] descriptions and signature data.
37#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
38pub struct ShieldedData {
39 /// The orchard flags for this transaction.
40 /// Denoted as `flagsOrchard` in the spec.
41 pub flags: Flags,
42 /// The net value of Orchard spends minus outputs.
43 /// Denoted as `valueBalanceOrchard` in the spec.
44 pub value_balance: Amount,
45 /// The shared anchor for all `Spend`s in this transaction.
46 /// Denoted as `anchorOrchard` in the spec.
47 pub shared_anchor: tree::Root,
48 /// The aggregated zk-SNARK proof for all the actions in this transaction.
49 /// Denoted as `proofsOrchard` in the spec.
50 pub proof: Halo2Proof,
51 /// The Orchard Actions, in the order they appear in the transaction.
52 /// Denoted as `vActionsOrchard` and `vSpendAuthSigsOrchard` in the spec.
53 pub actions: AtLeastOne<AuthorizedAction>,
54 /// A signature on the transaction `sighash`.
55 /// Denoted as `bindingSigOrchard` in the spec.
56 pub binding_sig: Signature<Binding>,
57}
58
59impl fmt::Display for ShieldedData {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 let mut fmter = f.debug_struct("orchard::ShieldedData");
62
63 fmter.field("actions", &self.actions.len());
64 fmter.field("value_balance", &self.value_balance);
65 fmter.field("flags", &self.flags);
66
67 fmter.field("proof_len", &self.proof.zcash_serialized_size());
68
69 fmter.field("shared_anchor", &self.shared_anchor);
70
71 fmter.finish()
72 }
73}
74
75impl ShieldedData {
76 /// Iterate over the [`Action`]s for the [`AuthorizedAction`]s in this
77 /// transaction, in the order they appear in it.
78 pub fn actions(&self) -> impl Iterator<Item = &Action> {
79 self.actions.actions()
80 }
81
82 /// Returns whether the proof has the canonical length for its number of actions.
83 ///
84 /// An Orchard proof is stored as an unbounded byte sequence, so a proof that is
85 /// present but not canonically sized can be padded with arbitrary trailing data
86 /// without affecting its validity. Bundles are parsed leniently (so that historical
87 /// transactions remain deserializable), so this is enforced separately as a
88 /// height-gated consensus rule. See `GHSA-jfw5-j458-pfv6`.
89 pub fn proof_size_is_canonical(&self) -> bool {
90 self.proof.0.len() == expected_proof_size(self.actions.len())
91 }
92
93 /// Collect the [`Nullifier`]s for this transaction.
94 pub fn nullifiers(&self) -> impl Iterator<Item = &Nullifier> {
95 self.actions().map(|action| &action.nullifier)
96 }
97
98 /// Calculate the Action binding verification key.
99 ///
100 /// Getting the binding signature validating key from the Action description
101 /// value commitments and the balancing value implicitly checks that the
102 /// balancing value is consistent with the value transferred in the
103 /// Action descriptions, but also proves that the signer knew the
104 /// randomness used for the Action value commitments, which
105 /// prevents replays of Action descriptions that perform an output.
106 /// In Orchard, all Action descriptions have a spend authorization signature,
107 /// therefore the proof of knowledge of the value commitment randomness
108 /// is less important, but stills provides defense in depth, and reduces the
109 /// differences between Orchard and Sapling.
110 ///
111 /// The net value of Orchard spends minus outputs in a transaction
112 /// is called the balancing value, measured in zatoshi as a signed integer
113 /// cv_balance.
114 ///
115 /// Consistency of cv_balance with the value commitments in Action
116 /// descriptions is enforced by the binding signature.
117 ///
118 /// Instead of generating a key pair at random, we generate it as a function
119 /// of the value commitments in the Action descriptions of the transaction, and
120 /// the balancing value.
121 ///
122 /// <https://zips.z.cash/protocol/protocol.pdf#orchardbalance>
123 pub fn binding_verification_key(&self) -> reddsa::VerificationKeyBytes<Binding> {
124 let cv: ValueCommitment = self.actions().map(|action| action.cv).sum();
125 let cv_balance: ValueCommitment =
126 ValueCommitment::new(pallas::Scalar::zero(), self.value_balance);
127
128 let key_bytes: [u8; 32] = (cv - cv_balance).into();
129 key_bytes.into()
130 }
131
132 /// Provide access to the `value_balance` field of the shielded data.
133 ///
134 /// Needed to calculate the sapling value balance.
135 pub fn value_balance(&self) -> Amount<NegativeAllowed> {
136 self.value_balance
137 }
138
139 /// Collect the cm_x's for this transaction, if it contains [`Action`]s with
140 /// outputs, in the order they appear in the transaction.
141 pub fn note_commitments(&self) -> impl Iterator<Item = &pallas::Base> {
142 self.actions().map(|action| &action.cm_x)
143 }
144}
145
146/// A trait for types that can provide Orchard actions.
147pub trait OrchardActions {
148 /// Returns an iterator over the actions in this type.
149 fn actions(&self) -> impl Iterator<Item = &Action> + '_;
150}
151
152impl OrchardActions for AtLeastOne<AuthorizedAction> {
153 /// Iterate over the [`Action`]s of each [`AuthorizedAction`].
154 fn actions(&self) -> impl Iterator<Item = &Action> + '_ {
155 self.iter()
156 .map(|authorized_action| &authorized_action.action)
157 }
158}
159
160/// An authorized action description.
161///
162/// Every authorized Orchard `Action` must have a corresponding `SpendAuth` signature.
163#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
164pub struct AuthorizedAction {
165 /// The action description of this Action.
166 pub action: Action,
167 /// The spend signature.
168 pub spend_auth_sig: Signature<SpendAuth>,
169}
170
171impl AuthorizedAction {
172 /// Split out the action and the signature for V5 transaction
173 /// serialization.
174 pub fn into_parts(self) -> (Action, Signature<SpendAuth>) {
175 (self.action, self.spend_auth_sig)
176 }
177
178 // Combine the action and the spend auth sig from V5 transaction
179 /// deserialization.
180 pub fn from_parts(action: Action, spend_auth_sig: Signature<SpendAuth>) -> AuthorizedAction {
181 AuthorizedAction {
182 action,
183 spend_auth_sig,
184 }
185 }
186}
187
188/// The size of a single Action
189///
190/// Actions are 5 * 32 + 580 + 80 bytes so the total size of each Action is 820 bytes.
191/// [7.5 Action Description Encoding and Consensus][ps]
192///
193/// [ps]: <https://zips.z.cash/protocol/nu5.pdf#actionencodingandconsensus>
194pub const ACTION_SIZE: u64 = 5 * 32 + 580 + 80;
195
196/// The size of a single `Signature<SpendAuth>`.
197///
198/// Each Signature is 64 bytes.
199/// [7.1 Transaction Encoding and Consensus][ps]
200///
201/// [ps]: <https://zips.z.cash/protocol/nu5.pdf#actionencodingandconsensus>
202pub const SPEND_AUTH_SIG_SIZE: u64 = 64;
203
204/// The size of a single AuthorizedAction
205///
206/// Each serialized `Action` has a corresponding `Signature<SpendAuth>`.
207pub const AUTHORIZED_ACTION_SIZE: u64 = ACTION_SIZE + SPEND_AUTH_SIG_SIZE;
208
209/// The maximum number of orchard actions in a valid Zcash on-chain transaction V5.
210///
211/// If a transaction contains more actions than can fit in maximally large block, it might be
212/// valid on the network and in the mempool, but it can never be mined into a block. So
213/// rejecting these large edge-case transactions can never break consensus.
214impl TrustedPreallocate for Action {
215 fn max_allocation() -> u64 {
216 // Since a serialized Vec<AuthorizedAction> uses at least one byte for its length,
217 // and the signature is required,
218 // a valid max allocation can never exceed this size
219 const MAX: u64 = (MAX_BLOCK_BYTES - 1) / AUTHORIZED_ACTION_SIZE;
220 // # Consensus
221 //
222 // > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16.
223 //
224 // https://zips.z.cash/protocol/protocol.pdf#txnconsensus
225 //
226 // This acts as nActionsOrchard and is therefore subject to the rule.
227 // The maximum value is actually smaller due to the block size limit,
228 // but we ensure the 2^16 limit with a static assertion.
229 static_assertions::const_assert!(MAX < (1 << 16));
230 MAX
231 }
232}
233
234impl TrustedPreallocate for Signature<SpendAuth> {
235 fn max_allocation() -> u64 {
236 // Each signature must have a corresponding action.
237 Action::max_allocation()
238 }
239}
240
241bitflags! {
242 /// Per-Transaction flags for Orchard.
243 ///
244 /// The spend and output flags are passed to the `Halo2Proof` verifier, which verifies
245 /// the relevant note spending and creation consensus rules.
246 ///
247 /// # Consensus
248 ///
249 /// > [NU5 onward] In a version 5 transaction, the reserved bits 2..7 of the flagsOrchard
250 /// > field MUST be zero.
251 ///
252 /// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
253 ///
254 /// ([`bitflags`](https://docs.rs/bitflags/1.2.1/bitflags/index.html) restricts its values to the
255 /// set of valid flags)
256 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
257 pub struct Flags: u8 {
258 /// Enable spending non-zero valued Orchard notes.
259 ///
260 /// "the `enableSpendsOrchard` flag, if present, MUST be 0 for coinbase transactions"
261 const ENABLE_SPENDS = 0b00000001;
262 /// Enable creating new non-zero valued Orchard notes.
263 const ENABLE_OUTPUTS = 0b00000010;
264 }
265}
266
267// We use the `bitflags 2.x` library to implement [`Flags`]. The
268// `2.x` version of the library uses a different serialization
269// format compared to `1.x`.
270// This manual implementation uses the `bitflags_serde_legacy` crate
271// to serialize `Flags` as `bitflags 1.x` would.
272impl serde::Serialize for Flags {
273 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
274 bitflags_serde_legacy::serialize(self, "Flags", serializer)
275 }
276}
277
278// We use the `bitflags 2.x` library to implement [`Flags`]. The
279// `2.x` version of the library uses a different deserialization
280// format compared to `1.x`.
281// This manual implementation uses the `bitflags_serde_legacy` crate
282// to deserialize `Flags` as `bitflags 1.x` would.
283impl<'de> serde::Deserialize<'de> for Flags {
284 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
285 bitflags_serde_legacy::deserialize("Flags", deserializer)
286 }
287}
288
289impl ZcashSerialize for Flags {
290 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
291 writer.write_u8(self.bits())?;
292
293 Ok(())
294 }
295}
296
297impl ZcashDeserialize for Flags {
298 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
299 // Consensus rule: "In a version 5 transaction,
300 // the reserved bits 2..7 of the flagsOrchard field MUST be zero."
301 // https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
302 Flags::from_bits(reader.read_u8()?)
303 .ok_or(SerializationError::Parse("invalid reserved orchard flags"))
304 }
305}