Skip to main content

orchard/note/
commitment.rs

1use core::iter;
2
3use bitvec::{array::BitArray, order::Lsb0};
4use group::ff::{PrimeField, PrimeFieldBits};
5use pasta_curves::pallas;
6use subtle::{ConstantTimeEq, CtOption};
7
8use crate::{
9    constants::{fixed_bases::NOTE_COMMITMENT_PERSONALIZATION, L_ORCHARD_BASE},
10    spec::extract_p,
11    value::NoteValue,
12};
13
14/// The trapdoor for a note commitment.
15#[derive(Clone, Debug)]
16pub struct NoteCommitTrapdoor(pub(super) pallas::Scalar);
17
18impl NoteCommitTrapdoor {
19    /// Returns the inner scalar value.
20    pub fn inner(&self) -> pallas::Scalar {
21        self.0
22    }
23}
24
25/// A commitment to a note.
26#[derive(Clone, Debug)]
27pub struct NoteCommitment(pub(super) pallas::Point);
28
29impl NoteCommitment {
30    /// Returns the inner Pallas curve point.
31    pub fn inner(&self) -> pallas::Point {
32        self.0
33    }
34}
35
36impl NoteCommitment {
37    /// $NoteCommit^Orchard$.
38    ///
39    /// Defined in [Zcash Protocol Spec ยง 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit].
40    ///
41    /// [concretesinsemillacommit]: https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit
42    pub(super) fn derive(
43        g_d: [u8; 32],
44        pk_d: [u8; 32],
45        v: NoteValue,
46        rho: pallas::Base,
47        psi: pallas::Base,
48        rcm: NoteCommitTrapdoor,
49    ) -> CtOption<Self> {
50        let domain = sinsemilla::CommitDomain::new(NOTE_COMMITMENT_PERSONALIZATION);
51        domain
52            .commit(
53                iter::empty()
54                    .chain(BitArray::<_, Lsb0>::new(g_d).iter().by_vals())
55                    .chain(BitArray::<_, Lsb0>::new(pk_d).iter().by_vals())
56                    .chain(v.to_le_bits().iter().by_vals())
57                    .chain(rho.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE))
58                    .chain(psi.to_le_bits().iter().by_vals().take(L_ORCHARD_BASE)),
59                &rcm.0,
60            )
61            .map(NoteCommitment)
62    }
63}
64
65/// The x-coordinate of the commitment to a note.
66#[derive(Copy, Clone, Debug)]
67pub struct ExtractedNoteCommitment(pub(super) pallas::Base);
68
69impl ExtractedNoteCommitment {
70    /// Deserialize the extracted note commitment from a byte array.
71    ///
72    /// This method enforces the [consensus rule][cmxcanon] that the
73    /// byte representation of cmx MUST be canonical.
74    ///
75    /// [cmxcanon]: https://zips.z.cash/protocol/protocol.pdf#actionencodingandconsensus
76    pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<Self> {
77        pallas::Base::from_repr(*bytes).map(ExtractedNoteCommitment)
78    }
79
80    /// Serialize the value commitment to its canonical byte representation.
81    pub fn to_bytes(self) -> [u8; 32] {
82        self.0.to_repr()
83    }
84}
85
86impl From<NoteCommitment> for ExtractedNoteCommitment {
87    fn from(cm: NoteCommitment) -> Self {
88        ExtractedNoteCommitment(extract_p(&cm.0))
89    }
90}
91
92impl ExtractedNoteCommitment {
93    /// Returns the inner field element.
94    pub fn inner(&self) -> pallas::Base {
95        self.0
96    }
97}
98
99impl From<&ExtractedNoteCommitment> for [u8; 32] {
100    fn from(cmx: &ExtractedNoteCommitment) -> Self {
101        cmx.to_bytes()
102    }
103}
104
105impl ConstantTimeEq for ExtractedNoteCommitment {
106    fn ct_eq(&self, other: &Self) -> subtle::Choice {
107        self.0.ct_eq(&other.0)
108    }
109}
110
111impl PartialEq for ExtractedNoteCommitment {
112    fn eq(&self, other: &Self) -> bool {
113        self.ct_eq(other).into()
114    }
115}
116
117impl Eq for ExtractedNoteCommitment {}