Skip to main content

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}