zebra_chain/block/
commitment.rs

1//! The Commitment enum, used for the corresponding block header field.
2
3use std::fmt;
4
5use hex::{FromHex, ToHex};
6use thiserror::Error;
7
8use crate::{
9    block::{self, merkle::AuthDataRoot},
10    parameters::{Network, NetworkUpgrade, NetworkUpgrade::*},
11    sapling,
12};
13
14/// Zcash blocks contain different kinds of commitments to their contents,
15/// depending on the network and height.
16///
17/// The `Header.commitment_bytes` field is interpreted differently, based on the
18/// network and height. The interpretation changes in the network upgrade
19/// activation block, or in the block immediately after network upgrade
20/// activation.
21#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
22pub enum Commitment {
23    /// [Pre-Sapling] "A reserved field, to be ignored."
24    ///
25    /// This field is not verified.
26    PreSaplingReserved([u8; 32]),
27
28    /// [Sapling and Blossom] The final Sapling treestate of this block.
29    ///
30    /// The root LEBS2OSP256(rt) of the Sapling note commitment tree
31    /// corresponding to the final Sapling treestate of this block.
32    ///
33    /// Subsequent `Commitment` variants also commit to the `FinalSaplingRoot`,
34    /// via their `EarliestSaplingRoot` and `LatestSaplingRoot` fields.
35    ///
36    /// Since Zebra checkpoints on Canopy, we don't need to validate this
37    /// field, but since it's included in the ChainHistoryRoot, we are
38    /// already calculating it, so we might as well validate it.
39    ///
40    /// TODO: this field is verified during semantic verification
41    FinalSaplingRoot(sapling::tree::Root),
42
43    /// [Heartwood activation block] Reserved field.
44    ///
45    /// The value of this field MUST be all zeroes.
46    ///
47    /// This MUST NOT be interpreted as a root hash.
48    /// See ZIP-221 for details.
49    ///
50    /// This field is verified in `Commitment::from_bytes`.
51    ChainHistoryActivationReserved,
52
53    /// [(Heartwood activation block + 1) to Canopy] The root of a Merkle
54    /// Mountain Range chain history tree.
55    ///
56    /// This root hash commits to various features of the chain's history,
57    /// including the Sapling commitment tree. This commitment supports the
58    /// FlyClient protocol. See ZIP-221 for details.
59    ///
60    /// The commitment in each block covers the chain history from the most
61    /// recent network upgrade, through to the previous block. In particular,
62    /// an activation block commits to the entire previous network upgrade, and
63    /// the block after activation commits only to the activation block. (And
64    /// therefore transitively to all previous network upgrades covered by a
65    /// chain history hash in their activation block, via the previous block
66    /// hash field.)
67    ///
68    /// Since Zebra's mandatory checkpoint includes Canopy activation, we only
69    /// need to verify the chain history root from `Canopy + 1 block` onwards,
70    /// using a new history tree based on the `Canopy` activation block.
71    ///
72    /// NU5 and later upgrades use the [`Commitment::ChainHistoryBlockTxAuthCommitment`]
73    /// variant.
74    ///
75    /// TODO: this field is verified during contextual verification
76    ChainHistoryRoot(ChainHistoryMmrRootHash),
77
78    /// [NU5 activation onwards] A commitment to:
79    /// - the chain history Merkle Mountain Range tree, and
80    /// - the auth data merkle tree covering this block.
81    ///
82    /// The chain history Merkle Mountain Range tree commits to the previous
83    /// block and all ancestors in the current network upgrade. (A new chain
84    /// history tree starts from each network upgrade's activation block.)
85    ///
86    /// The auth data merkle tree commits to this block.
87    ///
88    /// This commitment supports the FlyClient protocol and non-malleable
89    /// transaction IDs. See ZIP-221 and ZIP-244 for details.
90    ///
91    /// See also the [`Commitment::ChainHistoryRoot`] variant.
92    ///
93    /// TODO: this field is verified during contextual verification
94    ChainHistoryBlockTxAuthCommitment(ChainHistoryBlockTxAuthCommitmentHash),
95}
96
97/// The required value of reserved `Commitment`s.
98pub(crate) const CHAIN_HISTORY_ACTIVATION_RESERVED: [u8; 32] = [0; 32];
99
100impl Commitment {
101    /// Returns `bytes` as the Commitment variant for `network` and `height`.
102    //
103    // TODO: rename as from_bytes_in_serialized_order()
104    pub(super) fn from_bytes(
105        bytes: [u8; 32],
106        network: &Network,
107        height: block::Height,
108    ) -> Result<Commitment, CommitmentError> {
109        use Commitment::*;
110        use CommitmentError::*;
111
112        match NetworkUpgrade::current_with_activation_height(network, height) {
113            (Genesis | BeforeOverwinter | Overwinter, _) => Ok(PreSaplingReserved(bytes)),
114            (Sapling | Blossom, _) => match sapling::tree::Root::try_from(bytes) {
115                Ok(root) => Ok(FinalSaplingRoot(root)),
116                _ => Err(InvalidSapingRootBytes),
117            },
118            (Heartwood, activation_height) if height == activation_height => {
119                if bytes == CHAIN_HISTORY_ACTIVATION_RESERVED {
120                    Ok(ChainHistoryActivationReserved)
121                } else {
122                    Err(InvalidChainHistoryActivationReserved { actual: bytes })
123                }
124            }
125            // NetworkUpgrade::current() returns the latest network upgrade that's activated at the provided height, so
126            // on Regtest for heights above height 0, it could return NU6, and it's possible for the current network upgrade
127            // to be NU6 (or Canopy, or any network upgrade above Heartwood) at the Heartwood activation height.
128            (Canopy | Nu5 | Nu6 | Nu6_1 | Nu7, activation_height)
129                if height == activation_height
130                    && Some(height) == Heartwood.activation_height(network) =>
131            {
132                if bytes == CHAIN_HISTORY_ACTIVATION_RESERVED {
133                    Ok(ChainHistoryActivationReserved)
134                } else {
135                    Err(InvalidChainHistoryActivationReserved { actual: bytes })
136                }
137            }
138            (Heartwood | Canopy, _) => Ok(ChainHistoryRoot(ChainHistoryMmrRootHash(bytes))),
139            (Nu5 | Nu6 | Nu6_1 | Nu7, _) => Ok(ChainHistoryBlockTxAuthCommitment(
140                ChainHistoryBlockTxAuthCommitmentHash(bytes),
141            )),
142
143            #[cfg(zcash_unstable = "zfuture")]
144            (ZFuture, _) => Ok(ChainHistoryBlockTxAuthCommitment(
145                ChainHistoryBlockTxAuthCommitmentHash(bytes),
146            )),
147        }
148    }
149
150    /// Returns the serialized bytes for this Commitment.
151    //
152    // TODO: refactor as bytes_in_serialized_order(&self)
153    #[cfg(test)]
154    pub(super) fn to_bytes(self) -> [u8; 32] {
155        use Commitment::*;
156
157        match self {
158            PreSaplingReserved(bytes) => bytes,
159            FinalSaplingRoot(hash) => hash.0.into(),
160            ChainHistoryActivationReserved => CHAIN_HISTORY_ACTIVATION_RESERVED,
161            ChainHistoryRoot(hash) => hash.0,
162            ChainHistoryBlockTxAuthCommitment(hash) => hash.0,
163        }
164    }
165}
166
167/// The root hash of a Merkle Mountain Range chain history tree.
168// TODO:
169//    - add methods for maintaining the MMR peaks, and calculating the root
170//      hash from the current set of peaks
171//    - move to a separate file
172#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)]
173pub struct ChainHistoryMmrRootHash([u8; 32]);
174
175impl fmt::Display for ChainHistoryMmrRootHash {
176    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177        f.write_str(&self.encode_hex::<String>())
178    }
179}
180
181impl fmt::Debug for ChainHistoryMmrRootHash {
182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        f.debug_tuple("ChainHistoryMmrRootHash")
184            .field(&self.encode_hex::<String>())
185            .finish()
186    }
187}
188
189impl From<[u8; 32]> for ChainHistoryMmrRootHash {
190    fn from(hash: [u8; 32]) -> Self {
191        ChainHistoryMmrRootHash(hash)
192    }
193}
194
195impl From<ChainHistoryMmrRootHash> for [u8; 32] {
196    fn from(hash: ChainHistoryMmrRootHash) -> Self {
197        hash.0
198    }
199}
200
201impl ChainHistoryMmrRootHash {
202    /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte.
203    ///
204    /// Zebra displays transaction and block hashes in big-endian byte-order,
205    /// following the u256 convention set by Bitcoin and zcashd.
206    pub fn bytes_in_display_order(&self) -> [u8; 32] {
207        let mut reversed_bytes = self.0;
208        reversed_bytes.reverse();
209        reversed_bytes
210    }
211
212    /// Convert bytes in big-endian byte-order into a `ChainHistoryMmrRootHash`.
213    ///
214    /// Zebra displays transaction and block hashes in big-endian byte-order,
215    /// following the u256 convention set by Bitcoin and zcashd.
216    pub fn from_bytes_in_display_order(
217        bytes_in_display_order: &[u8; 32],
218    ) -> ChainHistoryMmrRootHash {
219        let mut internal_byte_order = *bytes_in_display_order;
220        internal_byte_order.reverse();
221
222        ChainHistoryMmrRootHash(internal_byte_order)
223    }
224
225    /// Returns the serialized bytes for this Commitment.
226    pub fn bytes_in_serialized_order(&self) -> [u8; 32] {
227        self.0
228    }
229}
230
231impl ToHex for &ChainHistoryMmrRootHash {
232    fn encode_hex<T: FromIterator<char>>(&self) -> T {
233        self.bytes_in_display_order().encode_hex()
234    }
235
236    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
237        self.bytes_in_display_order().encode_hex_upper()
238    }
239}
240
241impl ToHex for ChainHistoryMmrRootHash {
242    fn encode_hex<T: FromIterator<char>>(&self) -> T {
243        (&self).encode_hex()
244    }
245
246    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
247        (&self).encode_hex_upper()
248    }
249}
250
251impl FromHex for ChainHistoryMmrRootHash {
252    type Error = <[u8; 32] as FromHex>::Error;
253
254    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
255        let mut hash = <[u8; 32]>::from_hex(hex)?;
256        hash.reverse();
257
258        Ok(hash.into())
259    }
260}
261
262/// A block commitment to chain history and transaction auth.
263/// - the chain history tree for all ancestors in the current network upgrade,
264///   and
265/// - the transaction authorising data in this block.
266///
267/// Introduced in NU5.
268#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
269pub struct ChainHistoryBlockTxAuthCommitmentHash([u8; 32]);
270
271impl fmt::Display for ChainHistoryBlockTxAuthCommitmentHash {
272    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273        f.write_str(&self.encode_hex::<String>())
274    }
275}
276
277impl fmt::Debug for ChainHistoryBlockTxAuthCommitmentHash {
278    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
279        f.debug_tuple("ChainHistoryBlockTxAuthCommitmentHash")
280            .field(&self.encode_hex::<String>())
281            .finish()
282    }
283}
284
285impl From<[u8; 32]> for ChainHistoryBlockTxAuthCommitmentHash {
286    fn from(hash: [u8; 32]) -> Self {
287        ChainHistoryBlockTxAuthCommitmentHash(hash)
288    }
289}
290
291impl From<ChainHistoryBlockTxAuthCommitmentHash> for [u8; 32] {
292    fn from(hash: ChainHistoryBlockTxAuthCommitmentHash) -> Self {
293        hash.0
294    }
295}
296
297impl ChainHistoryBlockTxAuthCommitmentHash {
298    /// Compute the block commitment from the history tree root and the
299    /// authorization data root, as specified in [ZIP-244].
300    ///
301    /// `history_tree_root` is the root of the history tree up to and including
302    /// the *previous* block.
303    /// `auth_data_root` is the root of the Merkle tree of authorizing data
304    /// commmitments of each transaction in the *current* block.
305    ///
306    ///  [ZIP-244]: https://zips.z.cash/zip-0244#block-header-changes
307    pub fn from_commitments(
308        history_tree_root: &ChainHistoryMmrRootHash,
309        auth_data_root: &AuthDataRoot,
310    ) -> Self {
311        // > The value of this hash [hashBlockCommitments] is the BLAKE2b-256 hash personalized
312        // > by the string "ZcashBlockCommit" of the following elements:
313        // >   hashLightClientRoot (as described in ZIP 221)
314        // >   hashAuthDataRoot    (as described below)
315        // >   terminator          [0u8;32]
316        let hash_block_commitments: [u8; 32] = blake2b_simd::Params::new()
317            .hash_length(32)
318            .personal(b"ZcashBlockCommit")
319            .to_state()
320            .update(&<[u8; 32]>::from(*history_tree_root)[..])
321            .update(&<[u8; 32]>::from(*auth_data_root))
322            .update(&[0u8; 32])
323            .finalize()
324            .as_bytes()
325            .try_into()
326            .expect("32 byte array");
327        Self(hash_block_commitments)
328    }
329
330    /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte.
331    ///
332    /// Zebra displays transaction and block hashes in big-endian byte-order,
333    /// following the u256 convention set by Bitcoin and zcashd.
334    pub fn bytes_in_display_order(&self) -> [u8; 32] {
335        let mut reversed_bytes = self.0;
336        reversed_bytes.reverse();
337        reversed_bytes
338    }
339
340    /// Convert bytes in big-endian byte-order into a `ChainHistoryBlockTxAuthCommitmentHash`.
341    ///
342    /// Zebra displays transaction and block hashes in big-endian byte-order,
343    /// following the u256 convention set by Bitcoin and zcashd.
344    pub fn from_bytes_in_display_order(
345        bytes_in_display_order: &[u8; 32],
346    ) -> ChainHistoryBlockTxAuthCommitmentHash {
347        let mut internal_byte_order = *bytes_in_display_order;
348        internal_byte_order.reverse();
349
350        ChainHistoryBlockTxAuthCommitmentHash(internal_byte_order)
351    }
352
353    /// Returns the serialized bytes for this Commitment.
354    pub fn bytes_in_serialized_order(&self) -> [u8; 32] {
355        self.0
356    }
357}
358
359impl ToHex for &ChainHistoryBlockTxAuthCommitmentHash {
360    fn encode_hex<T: FromIterator<char>>(&self) -> T {
361        self.bytes_in_display_order().encode_hex()
362    }
363
364    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
365        self.bytes_in_display_order().encode_hex_upper()
366    }
367}
368
369impl ToHex for ChainHistoryBlockTxAuthCommitmentHash {
370    fn encode_hex<T: FromIterator<char>>(&self) -> T {
371        (&self).encode_hex()
372    }
373
374    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
375        (&self).encode_hex_upper()
376    }
377}
378
379impl FromHex for ChainHistoryBlockTxAuthCommitmentHash {
380    type Error = <[u8; 32] as FromHex>::Error;
381
382    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
383        let mut hash = <[u8; 32]>::from_hex(hex)?;
384        hash.reverse();
385
386        Ok(hash.into())
387    }
388}
389
390/// Errors that can occur when checking RootHash consensus rules.
391///
392/// Each error variant corresponds to a consensus rule, so enumerating
393/// all possible verification failures enumerates the consensus rules we
394/// implement, and ensures that we don't reject blocks or transactions
395/// for a non-enumerated reason.
396#[allow(missing_docs)]
397#[derive(Error, Clone, Debug, PartialEq, Eq)]
398pub enum CommitmentError {
399    #[error(
400        "invalid final sapling root: expected {:?}, actual: {:?}",
401        hex::encode(expected),
402        hex::encode(actual)
403    )]
404    InvalidFinalSaplingRoot {
405        // TODO: are these fields a security risk? If so, open a ticket to remove
406        // similar fields across Zebra
407        expected: [u8; 32],
408        actual: [u8; 32],
409    },
410
411    #[error("invalid chain history activation reserved block commitment: expected all zeroes, actual: {:?}",  hex::encode(actual))]
412    InvalidChainHistoryActivationReserved { actual: [u8; 32] },
413
414    #[error(
415        "invalid chain history root: expected {:?}, actual: {:?}",
416        hex::encode(expected),
417        hex::encode(actual)
418    )]
419    InvalidChainHistoryRoot {
420        expected: [u8; 32],
421        actual: [u8; 32],
422    },
423
424    #[error(
425        "invalid block commitment root: expected {:?}, actual: {:?}",
426        hex::encode(expected),
427        hex::encode(actual)
428    )]
429    InvalidChainHistoryBlockTxAuthCommitment {
430        expected: [u8; 32],
431        actual: [u8; 32],
432    },
433
434    #[error("missing required block height: block commitments can't be parsed without a block height, block hash: {block_hash:?}")]
435    MissingBlockHeight { block_hash: block::Hash },
436
437    #[error("provided bytes are not a valid sapling root")]
438    InvalidSapingRootBytes,
439}