Skip to main content

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