Skip to main content

zebra_chain/primitives/
zcash_note_encryption.rs

1//! Contains code that interfaces with the zcash_note_encryption crate from
2//! librustzcash.
3
4use std::ops::Deref;
5
6use crate::{
7    block::Height,
8    parameters::{Network, NetworkUpgrade},
9    transaction::Transaction,
10};
11
12/// Returns true if all Sapling, Orchard, or Ironwood outputs, if any, decrypt successfully
13/// with an all-zeroes outgoing viewing key.
14pub fn decrypts_successfully(tx: &Transaction, network: &Network, height: Height) -> bool {
15    let nu = NetworkUpgrade::current(network, height);
16
17    let null_sapling_ovk = sapling_crypto::keys::OutgoingViewingKey([0u8; 32]);
18
19    // Note that, since this function is used to validate coinbase transactions, we can ignore
20    // the "grace period" mentioned in ZIP-212.
21    let zip_212_enforcement = if nu >= NetworkUpgrade::Canopy {
22        sapling_crypto::note_encryption::Zip212Enforcement::On
23    } else {
24        sapling_crypto::note_encryption::Zip212Enforcement::Off
25    };
26
27    if let Some(bundle) = tx.inner().deref().sapling_bundle() {
28        for output in bundle.shielded_outputs().iter() {
29            let recovery = sapling_crypto::note_encryption::try_sapling_output_recovery(
30                &null_sapling_ovk,
31                output,
32                zip_212_enforcement,
33            );
34            if recovery.is_none() {
35                return false;
36            }
37        }
38    }
39
40    if let Some(bundle) = tx.inner().deref().orchard_bundle() {
41        for act in bundle.actions() {
42            if zcash_note_encryption::try_output_recovery_with_ovk(
43                &orchard::note_encryption::OrchardDomain::for_action(act),
44                &orchard::keys::OutgoingViewingKey::from([0u8; 32]),
45                act,
46                act.cv_net(),
47                &act.encrypted_note().out_ciphertext,
48            )
49            .is_none()
50            {
51                return false;
52            }
53        }
54    }
55
56    // From NU6.3, newly shielded coinbase value is routed to the Ironwood pool, so the coinbase
57    // output-decryptability rule must cover Ironwood actions too. The Ironwood bundle reuses the
58    // Orchard action shape but its notes use the `IronwoodDomain` (V3) note-plaintext version.
59    if let Some(bundle) = tx.ironwood_bundle() {
60        for act in bundle.actions() {
61            if zcash_note_encryption::try_output_recovery_with_ovk(
62                &orchard::note_encryption::IronwoodDomain::for_action(act),
63                &orchard::keys::OutgoingViewingKey::from([0u8; 32]),
64                act,
65                act.cv_net(),
66                &act.encrypted_note().out_ciphertext,
67            )
68            .is_none()
69            {
70                return false;
71            }
72        }
73    }
74
75    true
76}