sapling_crypto/value/
sums.rs

1use core::fmt::{self, Debug};
2use core::iter::Sum;
3use core::ops::{Add, AddAssign, Sub, SubAssign};
4
5use group::GroupEncoding;
6use redjubjub::Binding;
7
8use super::{NoteValue, ValueCommitTrapdoor, ValueCommitment};
9use crate::constants::VALUE_COMMITMENT_VALUE_GENERATOR;
10
11/// A value operation overflowed.
12#[derive(Debug)]
13pub struct OverflowError;
14
15impl fmt::Display for OverflowError {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        write!(f, "Sapling value operation overflowed")
18    }
19}
20
21#[cfg(feature = "std")]
22impl std::error::Error for OverflowError {}
23
24/// A sum of Sapling note values.
25///
26/// [Zcash Protocol Spec § 4.13: Balance and Binding Signature (Sapling)][saplingbalance]
27/// constrains the range of this type to between `[-(r_J - 1)/2..(r_J - 1)/2]` in the
28/// abstract protocol, and `[−38913406623490299131842..104805176454780817500623]` in the
29/// concrete Zcash protocol. We represent it as an `i128`, which has a range large enough
30/// to handle Zcash transactions while small enough to ensure the abstract protocol bounds
31/// are not breached.
32///
33/// [saplingbalance]: https://zips.z.cash/protocol/protocol.pdf#saplingbalance
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
35pub struct ValueSum(i128);
36
37impl ValueSum {
38    /// Initializes a sum of `NoteValue`s to zero.
39    pub fn zero() -> Self {
40        ValueSum(0)
41    }
42
43    /// Instantiates a value sum from a raw encoding.
44    ///
45    /// Only intended for use with PCZTs.
46    pub(crate) fn from_raw(value_sum: i128) -> Self {
47        Self(value_sum)
48    }
49
50    /// Extracts the raw encoding of this value sum.
51    ///
52    /// Only intended for use with PCZTs.
53    pub fn to_raw(self) -> i128 {
54        self.0
55    }
56}
57
58impl Add<NoteValue> for ValueSum {
59    type Output = Option<ValueSum>;
60
61    #[allow(clippy::suspicious_arithmetic_impl)]
62    fn add(self, rhs: NoteValue) -> Self::Output {
63        self.0.checked_add(rhs.0.into()).map(ValueSum)
64    }
65}
66
67impl Sub<NoteValue> for ValueSum {
68    type Output = Option<ValueSum>;
69
70    #[allow(clippy::suspicious_arithmetic_impl)]
71    fn sub(self, rhs: NoteValue) -> Self::Output {
72        self.0.checked_sub(rhs.0.into()).map(ValueSum)
73    }
74}
75
76impl<'a> Sum<&'a NoteValue> for Result<ValueSum, OverflowError> {
77    fn sum<I: Iterator<Item = &'a NoteValue>>(mut iter: I) -> Self {
78        iter.try_fold(ValueSum(0), |acc, v| acc + *v)
79            .ok_or(OverflowError)
80    }
81}
82
83impl Sum<NoteValue> for Result<ValueSum, OverflowError> {
84    fn sum<I: Iterator<Item = NoteValue>>(mut iter: I) -> Self {
85        iter.try_fold(ValueSum(0), |acc, v| acc + v)
86            .ok_or(OverflowError)
87    }
88}
89
90impl TryFrom<ValueSum> for i64 {
91    type Error = OverflowError;
92
93    fn try_from(v: ValueSum) -> Result<i64, Self::Error> {
94        i64::try_from(v.0).map_err(|_| OverflowError)
95    }
96}
97
98/// A sum of Sapling value commitment blinding factors.
99#[derive(Clone, Copy, Debug)]
100pub struct TrapdoorSum(jubjub::Scalar);
101
102impl TrapdoorSum {
103    /// Initializes a sum of `ValueCommitTrapdoor`s to zero.
104    pub fn zero() -> Self {
105        TrapdoorSum(jubjub::Scalar::zero())
106    }
107
108    /// Transform this trapdoor sum into the corresponding RedJubjub private key.
109    ///
110    /// This is public for access by `zcash_proofs`.
111    pub fn into_bsk(self) -> redjubjub::SigningKey<Binding> {
112        redjubjub::SigningKey::try_from(self.0.to_bytes())
113            .expect("valid scalars are valid signing keys")
114    }
115}
116
117impl Add<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
118    type Output = TrapdoorSum;
119
120    fn add(self, rhs: &Self) -> Self::Output {
121        TrapdoorSum(self.0 + rhs.0)
122    }
123}
124
125impl Add<&ValueCommitTrapdoor> for TrapdoorSum {
126    type Output = TrapdoorSum;
127
128    fn add(self, rhs: &ValueCommitTrapdoor) -> Self::Output {
129        TrapdoorSum(self.0 + rhs.0)
130    }
131}
132
133impl AddAssign<&ValueCommitTrapdoor> for TrapdoorSum {
134    fn add_assign(&mut self, rhs: &ValueCommitTrapdoor) {
135        self.0 += rhs.0;
136    }
137}
138
139impl Sub<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
140    type Output = TrapdoorSum;
141
142    fn sub(self, rhs: &Self) -> Self::Output {
143        TrapdoorSum(self.0 - rhs.0)
144    }
145}
146
147impl Sub<TrapdoorSum> for TrapdoorSum {
148    type Output = TrapdoorSum;
149
150    fn sub(self, rhs: Self) -> Self::Output {
151        TrapdoorSum(self.0 - rhs.0)
152    }
153}
154
155impl SubAssign<&ValueCommitTrapdoor> for TrapdoorSum {
156    fn sub_assign(&mut self, rhs: &ValueCommitTrapdoor) {
157        self.0 -= rhs.0;
158    }
159}
160
161impl<'a> Sum<&'a ValueCommitTrapdoor> for TrapdoorSum {
162    fn sum<I: Iterator<Item = &'a ValueCommitTrapdoor>>(iter: I) -> Self {
163        iter.fold(TrapdoorSum::zero(), |acc, cv| acc + cv)
164    }
165}
166
167/// A sum of Sapling value commitments.
168#[derive(Clone, Copy, Debug)]
169pub struct CommitmentSum(jubjub::ExtendedPoint);
170
171impl CommitmentSum {
172    /// Initializes a sum of `ValueCommitment`s to zero.
173    pub fn zero() -> Self {
174        CommitmentSum(jubjub::ExtendedPoint::identity())
175    }
176
177    /// Transform this value commitment sum into the corresponding RedJubjub public key.
178    ///
179    /// This is public for access by `zcash_proofs`.
180    pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::VerificationKey<Binding> {
181        let value: i64 = value_balance.into();
182
183        // Compute the absolute value.
184        let abs_value = match value.checked_abs() {
185            Some(v) => u64::try_from(v).expect("v is non-negative"),
186            None => 1u64 << 63,
187        };
188
189        // Construct the field representation of the signed value.
190        let value_balance = if value.is_negative() {
191            -jubjub::Scalar::from(abs_value)
192        } else {
193            jubjub::Scalar::from(abs_value)
194        };
195
196        // Subtract `value_balance` from the sum to get the final bvk.
197        let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance;
198
199        redjubjub::VerificationKey::try_from(bvk.to_bytes())
200            .expect("valid points are valid verification keys")
201    }
202}
203
204impl Add<&ValueCommitment> for ValueCommitment {
205    type Output = CommitmentSum;
206
207    fn add(self, rhs: &Self) -> Self::Output {
208        CommitmentSum(self.0 + rhs.0)
209    }
210}
211
212impl Add<&ValueCommitment> for CommitmentSum {
213    type Output = CommitmentSum;
214
215    fn add(self, rhs: &ValueCommitment) -> Self::Output {
216        CommitmentSum(self.0 + rhs.0)
217    }
218}
219
220impl AddAssign<&ValueCommitment> for CommitmentSum {
221    fn add_assign(&mut self, rhs: &ValueCommitment) {
222        self.0 += rhs.0;
223    }
224}
225
226impl Sub<&ValueCommitment> for ValueCommitment {
227    type Output = CommitmentSum;
228
229    fn sub(self, rhs: &Self) -> Self::Output {
230        CommitmentSum(self.0 - rhs.0)
231    }
232}
233
234impl SubAssign<&ValueCommitment> for CommitmentSum {
235    fn sub_assign(&mut self, rhs: &ValueCommitment) {
236        self.0 -= rhs.0;
237    }
238}
239
240impl Sub<CommitmentSum> for CommitmentSum {
241    type Output = CommitmentSum;
242
243    fn sub(self, rhs: Self) -> Self::Output {
244        CommitmentSum(self.0 - rhs.0)
245    }
246}
247
248impl Sum<ValueCommitment> for CommitmentSum {
249    fn sum<I: Iterator<Item = ValueCommitment>>(iter: I) -> Self {
250        iter.fold(CommitmentSum::zero(), |acc, cv| acc + &cv)
251    }
252}
253
254impl<'a> Sum<&'a ValueCommitment> for CommitmentSum {
255    fn sum<I: Iterator<Item = &'a ValueCommitment>>(iter: I) -> Self {
256        iter.fold(CommitmentSum::zero(), |acc, cv| acc + cv)
257    }
258}