1use 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#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
22pub enum Commitment {
23 PreSaplingReserved([u8; 32]),
27
28 FinalSaplingRoot(sapling::tree::Root),
42
43 ChainHistoryActivationReserved,
52
53 ChainHistoryRoot(ChainHistoryMmrRootHash),
77
78 ChainHistoryBlockTxAuthCommitment(ChainHistoryBlockTxAuthCommitmentHash),
95}
96
97pub(crate) const CHAIN_HISTORY_ACTIVATION_RESERVED: [u8; 32] = [0; 32];
99
100impl Commitment {
101 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 (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 #[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#[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 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 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 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#[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 pub fn from_commitments(
308 history_tree_root: &ChainHistoryMmrRootHash,
309 auth_data_root: &AuthDataRoot,
310 ) -> Self {
311 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 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 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 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#[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 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}