1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
//! Sapling shielded data for `V4` and `V5` `Transaction`s.
//!
//! Zebra uses a generic shielded data type for `V4` and `V5` transactions.
//! The `value_balance` change is handled using the default zero value.
//! The anchor change is handled using the `AnchorVariant` type trait.

use std::{
    cmp::{max, Eq, PartialEq},
    fmt::{self, Debug},
};

use itertools::Itertools;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
use serde::{de::DeserializeOwned, Serialize};

use crate::{
    amount::{Amount, NegativeAllowed},
    primitives::{
        redjubjub::{Binding, Signature},
        Groth16Proof,
    },
    sapling::{
        output::OutputPrefixInTransactionV5, spend::SpendPrefixInTransactionV5, tree, Nullifier,
        Output, Spend, ValueCommitment,
    },
    serialization::{AtLeastOne, TrustedPreallocate},
};

/// Per-Spend Sapling anchors, used in Transaction V4 and the
/// `spends_per_anchor` method.
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct PerSpendAnchor {}

/// Shared Sapling anchors, used in Transaction V5.
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SharedAnchor {}

/// This field is not present in this transaction version.
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct FieldNotPresent;

impl AnchorVariant for PerSpendAnchor {
    type Shared = FieldNotPresent;
    type PerSpend = tree::Root;
}

impl AnchorVariant for SharedAnchor {
    type Shared = tree::Root;
    type PerSpend = FieldNotPresent;
}

/// A type trait to handle structural differences between V4 and V5 Sapling
/// Transaction anchors.
///
/// In Transaction V4, anchors are per-Spend. In Transaction V5, there is a
/// single transaction anchor for all Spends in a transaction.
pub trait AnchorVariant {
    /// The type of the shared anchor.
    type Shared: Clone + Debug + DeserializeOwned + Serialize + Eq + PartialEq;

    /// The type of the per-spend anchor.
    type PerSpend: Clone + Debug + DeserializeOwned + Serialize + Eq + PartialEq;
}

/// A bundle of [`Spend`] and [`Output`] descriptions and signature data.
///
/// Spend and Output descriptions are optional, but Zcash transactions must
/// include a binding signature if and only if there is at least one Spend *or*
/// Output description. This wrapper type bundles at least one Spend or Output
/// description with the required signature data, so that an
/// `Option<ShieldedData>` correctly models the presence or absence of any
/// shielded data.
///
/// # Differences between Transaction Versions
///
/// The Sapling `value_balance` field is optional in `Transaction::V5`, but
/// required in `Transaction::V4`. In both cases, if there is no `ShieldedData`,
/// then the field value must be zero. Therefore, we only need to store
/// `value_balance` when there is some Sapling `ShieldedData`.
///
/// In `Transaction::V4`, each `Spend` has its own anchor. In `Transaction::V5`,
/// there is a single `shared_anchor` for the entire transaction, which is only
/// present when there is at least one spend. These structural differences are
/// modeled using the `AnchorVariant` type trait and `TransferData` enum.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ShieldedData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
{
    /// The net value of Sapling spend transfers minus output transfers.
    /// Denoted as `valueBalanceSapling` in the spec.
    pub value_balance: Amount,

    /// A bundle of spends and outputs, containing at least one spend or
    /// output, in the order they appear in the transaction.
    ///
    /// In V5 transactions, also contains a shared anchor, if there are any
    /// spends.
    pub transfers: TransferData<AnchorV>,

    /// A signature on the transaction hash.
    /// Denoted as `bindingSigSapling` in the spec.
    pub binding_sig: Signature<Binding>,
}

/// A bundle of [`Spend`] and [`Output`] descriptions, and a shared anchor.
///
/// This wrapper type bundles at least one Spend or Output description with
/// the required anchor data, so that an `Option<ShieldedData>` (which contains
/// this type) correctly models the presence or absence of any spends and
/// shielded data, across both V4 and V5 transactions.
///
/// Specifically, TransferData ensures that:
/// * there is at least one spend or output, and
/// * the shared anchor is only present when there are spends.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum TransferData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
{
    /// A bundle containing at least one spend, and the shared spend anchor.
    /// There can also be zero or more outputs.
    ///
    /// In Transaction::V5, if there are any spends, there must also be a shared
    /// spend anchor.
    SpendsAndMaybeOutputs {
        /// The shared anchor for all `Spend`s in this transaction.
        ///
        /// The anchor is the root of the Sapling note commitment tree in a previous
        /// block. This root should be in the best chain for a transaction to be
        /// mined, and it must be in the relevant chain for a transaction to be
        /// valid.
        ///
        /// Some transaction versions have a per-spend anchor, rather than a shared
        /// anchor.
        ///
        /// Use the `shared_anchor` method to access this field.
        shared_anchor: AnchorV::Shared,

        /// At least one spend.
        ///
        /// Use the [`ShieldedData::spends`] method to get an iterator over the
        /// [`Spend`]s in this `TransferData`.
        spends: AtLeastOne<Spend<AnchorV>>,

        /// Maybe some outputs (can be empty), in the order they appear in the
        /// transaction.
        ///
        /// Use the [`ShieldedData::outputs`] method to get an iterator over the
        /// [`Output`]s in this `TransferData`.
        maybe_outputs: Vec<Output>,
    },

    /// A bundle containing at least one output, with no spends and no shared
    /// spend anchor.
    ///
    /// In Transaction::V5, if there are no spends, there must not be a shared
    /// anchor.
    JustOutputs {
        /// At least one output, in the order they appear in the transaction.
        ///
        /// Use the [`ShieldedData::outputs`] method to get an iterator over the
        /// [`Output`]s in this `TransferData`.
        outputs: AtLeastOne<Output>,
    },
}

impl<AnchorV> fmt::Display for ShieldedData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut fmter = f.debug_struct(
            format!(
                "sapling::ShieldedData<{}>",
                std::any::type_name::<AnchorV>()
            )
            .as_str(),
        );

        fmter.field("spends", &self.transfers.spends().count());
        fmter.field("outputs", &self.transfers.outputs().count());
        fmter.field("value_balance", &self.value_balance);

        fmter.finish()
    }
}

impl<AnchorV> ShieldedData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
    Spend<PerSpendAnchor>: From<(Spend<AnchorV>, AnchorV::Shared)>,
{
    /// Iterate over the [`Spend`]s for this transaction, returning deduplicated
    /// [`tree::Root`]s, regardless of the underlying transaction version.
    pub fn anchors(&self) -> impl Iterator<Item = tree::Root> + '_ {
        // TODO: use TransferData::shared_anchor to improve performance for V5 transactions
        self.spends_per_anchor()
            .map(|spend| spend.per_spend_anchor)
            .unique_by(|raw| raw.0.to_bytes())
    }

    /// Iterate over the [`Spend`]s for this transaction, returning
    /// `Spend<PerSpendAnchor>` regardless of the underlying transaction version.
    ///
    /// # Correctness
    ///
    /// Do not use this function for serialization.
    pub fn spends_per_anchor(&self) -> impl Iterator<Item = Spend<PerSpendAnchor>> + '_ {
        self.transfers.spends_per_anchor()
    }
}

impl<AnchorV> ShieldedData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
{
    /// Iterate over the [`Spend`]s for this transaction, returning them as
    /// their generic type.
    ///
    /// # Correctness
    ///
    /// Use this function for serialization.
    pub fn spends(&self) -> impl Iterator<Item = &Spend<AnchorV>> {
        self.transfers.spends()
    }

    /// Iterate over the [`Output`]s for this transaction, in the order they
    /// appear in it.
    pub fn outputs(&self) -> impl Iterator<Item = &Output> {
        self.transfers.outputs()
    }

    /// Provide the shared anchor for this transaction, if present.
    ///
    /// The shared anchor is only present if:
    /// * there is at least one spend, and
    /// * this is a `V5` transaction.
    pub fn shared_anchor(&self) -> Option<AnchorV::Shared> {
        self.transfers.shared_anchor()
    }

    /// Collect the [`Nullifier`]s for this transaction, if it contains
    /// [`Spend`]s.
    pub fn nullifiers(&self) -> impl Iterator<Item = &Nullifier> {
        self.spends().map(|spend| &spend.nullifier)
    }

    /// Collect the cm_u's for this transaction, if it contains [`Output`]s,
    /// in the order they appear in the transaction.
    pub fn note_commitments(&self) -> impl Iterator<Item = &jubjub::Fq> {
        self.outputs().map(|output| &output.cm_u)
    }

    /// Calculate the Spend/Output binding verification key.
    ///
    /// Getting the binding signature validating key from the Spend and Output
    /// description value commitments and the balancing value implicitly checks
    /// that the balancing value is consistent with the value transferred in the
    /// Spend and Output descriptions but also proves that the signer knew the
    /// randomness used for the Spend and Output value commitments, which
    /// prevents replays of Output descriptions.
    ///
    /// The net value of Spend transfers minus Output transfers in a transaction
    /// is called the balancing value, measured in zatoshi as a signed integer
    /// v_balance.
    ///
    /// Consistency of v_balance with the value commitments in Spend
    /// descriptions and Output descriptions is enforced by the binding
    /// signature.
    ///
    /// Instead of generating a key pair at random, we generate it as a function
    /// of the value commitments in the Spend descriptions and Output
    /// descriptions of the transaction, and the balancing value.
    ///
    /// <https://zips.z.cash/protocol/protocol.pdf#saplingbalance>
    pub fn binding_verification_key(&self) -> redjubjub::VerificationKeyBytes<Binding> {
        let cv_old: ValueCommitment = self.spends().map(|spend| spend.cv.into()).sum();
        let cv_new: ValueCommitment = self.outputs().map(|output| output.cv.into()).sum();
        let cv_balance: ValueCommitment =
            ValueCommitment::new(jubjub::Fr::zero(), self.value_balance);

        let key_bytes: [u8; 32] = (cv_old - cv_new - cv_balance).into();

        key_bytes.into()
    }

    /// Provide access to the `value_balance` field of the shielded data.
    ///
    /// Needed to calculate the sapling value balance.
    pub fn value_balance(&self) -> Amount<NegativeAllowed> {
        self.value_balance
    }
}

impl<AnchorV> TransferData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
    Spend<PerSpendAnchor>: From<(Spend<AnchorV>, AnchorV::Shared)>,
{
    /// Iterate over the [`Spend`]s for this transaction, returning
    /// `Spend<PerSpendAnchor>` regardless of the underlying transaction version.
    ///
    /// Allows generic operations over V4 and V5 transactions, including:
    /// * spend verification, and
    /// * non-malleable transaction ID generation.
    ///
    /// # Correctness
    ///
    /// Do not use this function for serialization.
    pub fn spends_per_anchor(&self) -> impl Iterator<Item = Spend<PerSpendAnchor>> + '_ {
        self.spends().cloned().map(move |spend| {
            Spend::<PerSpendAnchor>::from((
                spend,
                self.shared_anchor()
                    .expect("shared anchor must be Some if there are any spends"),
            ))
        })
    }
}

impl<AnchorV> TransferData<AnchorV>
where
    AnchorV: AnchorVariant + Clone,
{
    /// Iterate over the [`Spend`]s for this transaction, returning them as
    /// their generic type.
    pub fn spends(&self) -> impl Iterator<Item = &Spend<AnchorV>> {
        use TransferData::*;

        let spends = match self {
            SpendsAndMaybeOutputs { spends, .. } => Some(spends.iter()),
            JustOutputs { .. } => None,
        };

        // this awkward construction avoids returning a newtype struct or
        // type-erased boxed iterator
        spends.into_iter().flatten()
    }

    /// Iterate over the [`Output`]s for this transaction.
    pub fn outputs(&self) -> impl Iterator<Item = &Output> {
        use TransferData::*;

        match self {
            SpendsAndMaybeOutputs { maybe_outputs, .. } => maybe_outputs,
            JustOutputs { outputs, .. } => outputs.as_vec(),
        }
        .iter()
    }

    /// Provide the shared anchor for this transaction, if present.
    ///
    /// The shared anchor is only present if:
    /// * there is at least one spend, and
    /// * this is a `V5` transaction.
    pub fn shared_anchor(&self) -> Option<AnchorV::Shared> {
        use TransferData::*;

        match self {
            SpendsAndMaybeOutputs { shared_anchor, .. } => Some(shared_anchor.clone()),
            JustOutputs { .. } => None,
        }
    }
}

impl TrustedPreallocate for Groth16Proof {
    fn max_allocation() -> u64 {
        // Each V5 transaction proof array entry must have a corresponding
        // spend or output prefix. We use the larger limit, so we don't reject
        // any valid large blocks.
        //
        // TODO: put a separate limit on proofs in spends and outputs
        max(
            SpendPrefixInTransactionV5::max_allocation(),
            OutputPrefixInTransactionV5::max_allocation(),
        )
    }
}