zebra_chain/sapling/
commitment.rs

1//! Note and value commitments.
2
3use std::io;
4
5use hex::{FromHex, FromHexError, ToHex};
6
7use crate::serialization::{serde_helpers, SerializationError, ZcashDeserialize, ZcashSerialize};
8
9#[cfg(test)]
10mod test_vectors;
11
12/// The randomness used in the Pedersen Hash for note commitment.
13///
14/// Equivalent to `sapling_crypto::note::CommitmentRandomness`,
15/// but we can't use it directly as it is not public.
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub struct CommitmentRandomness(jubjub::Fr);
18
19/// A wrapper for the `sapling_crypto::value::ValueCommitment` type.
20///
21/// We need the wrapper to derive Serialize, Deserialize and Equality.
22#[derive(Clone, Debug, Deserialize, Serialize)]
23pub struct ValueCommitment(
24    #[serde(with = "serde_helpers::ValueCommitment")] pub sapling_crypto::value::ValueCommitment,
25);
26
27impl PartialEq for ValueCommitment {
28    fn eq(&self, other: &Self) -> bool {
29        self.0.as_inner() == other.0.as_inner()
30    }
31}
32impl Eq for ValueCommitment {}
33
34impl ValueCommitment {
35    /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte.
36    ///
37    /// Zebra displays commitment value in big-endian byte-order,
38    /// following the convention set by zcashd.
39    pub fn bytes_in_display_order(&self) -> [u8; 32] {
40        let mut reversed_bytes = self.0.to_bytes();
41        reversed_bytes.reverse();
42        reversed_bytes
43    }
44}
45
46impl ToHex for &ValueCommitment {
47    fn encode_hex<T: FromIterator<char>>(&self) -> T {
48        self.bytes_in_display_order().encode_hex()
49    }
50
51    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
52        self.bytes_in_display_order().encode_hex_upper()
53    }
54}
55
56impl FromHex for ValueCommitment {
57    type Error = FromHexError;
58
59    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
60        // Parse hex string to 32 bytes
61        let mut bytes = <[u8; 32]>::from_hex(hex)?;
62        // Convert from big-endian (display) to little-endian (internal)
63        bytes.reverse();
64
65        Self::zcash_deserialize(io::Cursor::new(&bytes))
66            .map_err(|_| FromHexError::InvalidStringLength)
67    }
68}
69
70#[cfg(any(test, feature = "proptest-impl"))]
71impl From<jubjub::ExtendedPoint> for ValueCommitment {
72    /// Convert a Jubjub point into a ValueCommitment.
73    ///
74    /// # Panics
75    ///
76    /// Panics if the given point does not correspond to a valid ValueCommitment.
77    fn from(extended_point: jubjub::ExtendedPoint) -> Self {
78        let bytes = jubjub::AffinePoint::from(extended_point).to_bytes();
79
80        let value_commitment =
81            sapling_crypto::value::ValueCommitment::from_bytes_not_small_order(&bytes)
82                .into_option()
83                .expect("invalid ValueCommitment bytes");
84
85        ValueCommitment(value_commitment)
86    }
87}
88
89impl ZcashDeserialize for sapling_crypto::value::ValueCommitment {
90    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
91        let mut buf = [0u8; 32];
92        reader.read_exact(&mut buf)?;
93
94        let value_commitment: Option<sapling_crypto::value::ValueCommitment> =
95            sapling_crypto::value::ValueCommitment::from_bytes_not_small_order(&buf).into_option();
96
97        value_commitment.ok_or(SerializationError::Parse("invalid ValueCommitment bytes"))
98    }
99}
100
101impl ZcashDeserialize for ValueCommitment {
102    fn zcash_deserialize<R: io::Read>(reader: R) -> Result<Self, SerializationError> {
103        let value_commitment = sapling_crypto::value::ValueCommitment::zcash_deserialize(reader)?;
104        Ok(Self(value_commitment))
105    }
106}
107
108impl ZcashSerialize for ValueCommitment {
109    fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
110        writer.write_all(&self.0.to_bytes())?;
111        Ok(())
112    }
113}
114
115impl ZcashDeserialize for sapling_crypto::note::ExtractedNoteCommitment {
116    fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
117        let mut buf = [0u8; 32];
118        reader.read_exact(&mut buf)?;
119
120        let extracted_note_commitment: Option<sapling_crypto::note::ExtractedNoteCommitment> =
121            sapling_crypto::note::ExtractedNoteCommitment::from_bytes(&buf).into_option();
122
123        extracted_note_commitment.ok_or(SerializationError::Parse(
124            "invalid ExtractedNoteCommitment bytes",
125        ))
126    }
127}