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    pub(crate) fn from_raw(value_sum: i128) -> Self {
55        Self(value_sum)
56    }
57
58    /// Extracts the raw encoding of this value sum.
59    ///
60    /// Only intended for use with PCZTs.
61    pub fn to_raw(self) -> i128 {
62        self.0
63    }
64}
65
66impl Add<NoteValue> for ValueSum {
67    type Output = Option<ValueSum>;
68
69    #[allow(clippy::suspicious_arithmetic_impl)]
70    fn add(self, rhs: NoteValue) -> Self::Output {
71        self.0.checked_add(rhs.0.into()).map(ValueSum)
72    }
73}
74
75impl Sub<NoteValue> for ValueSum {
76    type Output = Option<ValueSum>;
77
78    #[allow(clippy::suspicious_arithmetic_impl)]
79    fn sub(self, rhs: NoteValue) -> Self::Output {
80        self.0.checked_sub(rhs.0.into()).map(ValueSum)
81    }
82}
83
84impl<'a> Sum<&'a NoteValue> for Result<ValueSum, BalanceError> {
85    fn sum<I: Iterator<Item = &'a NoteValue>>(mut iter: I) -> Self {
86        iter.try_fold(ValueSum(0), |acc, v| acc + *v)
87            .ok_or(BalanceError::Overflow)
88    }
89}
90
91impl Sum<NoteValue> for Result<ValueSum, BalanceError> {
92    fn sum<I: Iterator<Item = NoteValue>>(mut iter: I) -> Self {
93        iter.try_fold(ValueSum(0), |acc, v| acc + v)
94            .ok_or(BalanceError::Overflow)
95    }
96}
97
98impl TryFrom<ValueSum> for i64 {
99    type Error = BalanceError;
100
101    fn try_from(v: ValueSum) -> Result<i64, Self::Error> {
102        i64::try_from(v.0).map_err(|_| BalanceError::Overflow)
103    }
104}
105
106/// A sum of Sapling value commitment blinding factors.
107#[derive(Clone, Copy, Debug)]
108pub struct TrapdoorSum(jubjub::Scalar);
109
110impl TrapdoorSum {
111    /// Initializes a sum of `ValueCommitTrapdoor`s to zero.
112    pub fn zero() -> Self {
113        TrapdoorSum(jubjub::Scalar::zero())
114    }
115
116    /// Transform this trapdoor sum into the corresponding RedJubjub private key.
117    ///
118    /// This is public for access by `zcash_proofs`.
119    pub fn into_bsk(self) -> redjubjub::SigningKey<Binding> {
120        redjubjub::SigningKey::try_from(self.0.to_bytes())
121            .expect("valid scalars are valid signing keys")
122    }
123}
124
125impl Add<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
126    type Output = TrapdoorSum;
127
128    fn add(self, rhs: &Self) -> Self::Output {
129        TrapdoorSum(self.0 + rhs.0)
130    }
131}
132
133impl Add<&ValueCommitTrapdoor> for TrapdoorSum {
134    type Output = TrapdoorSum;
135
136    fn add(self, rhs: &ValueCommitTrapdoor) -> Self::Output {
137        TrapdoorSum(self.0 + rhs.0)
138    }
139}
140
141impl AddAssign<&ValueCommitTrapdoor> for TrapdoorSum {
142    fn add_assign(&mut self, rhs: &ValueCommitTrapdoor) {
143        self.0 += rhs.0;
144    }
145}
146
147impl Sub<&ValueCommitTrapdoor> for ValueCommitTrapdoor {
148    type Output = TrapdoorSum;
149
150    fn sub(self, rhs: &Self) -> Self::Output {
151        TrapdoorSum(self.0 - rhs.0)
152    }
153}
154
155impl Sub<TrapdoorSum> for TrapdoorSum {
156    type Output = TrapdoorSum;
157
158    fn sub(self, rhs: Self) -> Self::Output {
159        TrapdoorSum(self.0 - rhs.0)
160    }
161}
162
163impl SubAssign<&ValueCommitTrapdoor> for TrapdoorSum {
164    fn sub_assign(&mut self, rhs: &ValueCommitTrapdoor) {
165        self.0 -= rhs.0;
166    }
167}
168
169impl<'a> Sum<&'a ValueCommitTrapdoor> for TrapdoorSum {
170    fn sum<I: Iterator<Item = &'a ValueCommitTrapdoor>>(iter: I) -> Self {
171        iter.fold(TrapdoorSum::zero(), |acc, cv| acc + cv)
172    }
173}
174
175/// A sum of Sapling value commitments.
176#[derive(Clone, Copy, Debug)]
177pub struct CommitmentSum(jubjub::ExtendedPoint);
178
179impl CommitmentSum {
180    /// Initializes a sum of `ValueCommitment`s to zero.
181    pub fn zero() -> Self {
182        CommitmentSum(jubjub::ExtendedPoint::identity())
183    }
184
185    /// Transform this value commitment sum into the corresponding RedJubjub public key.
186    ///
187    /// This is public for access by `zcash_proofs`.
188    pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::VerificationKey<Binding> {
189        let value: i64 = value_balance.into();
190
191        // Compute the absolute value.
192        let abs_value = match value.checked_abs() {
193            Some(v) => u64::try_from(v).expect("v is non-negative"),
194            None => 1u64 << 63,
195        };
196
197        // Construct the field representation of the signed value.
198        let value_balance = if value.is_negative() {
199            -jubjub::Scalar::from(abs_value)
200        } else {
201            jubjub::Scalar::from(abs_value)
202        };
203
204        // Subtract `value_balance` from the sum to get the final bvk.
205        let bvk = self.0 - VALUE_COMMITMENT_VALUE_GENERATOR * value_balance;
206
207        redjubjub::VerificationKey::try_from(bvk.to_bytes())
208            .expect("valid points are valid verification keys")
209    }
210}
211
212impl Add<&ValueCommitment> for ValueCommitment {
213    type Output = CommitmentSum;
214
215    fn add(self, rhs: &Self) -> Self::Output {
216        CommitmentSum(self.0 + rhs.0)
217    }
218}
219
220impl Add<&ValueCommitment> for CommitmentSum {
221    type Output = CommitmentSum;
222
223    fn add(self, rhs: &ValueCommitment) -> Self::Output {
224        CommitmentSum(self.0 + rhs.0)
225    }
226}
227
228impl AddAssign<&ValueCommitment> for CommitmentSum {
229    fn add_assign(&mut self, rhs: &ValueCommitment) {
230        self.0 += rhs.0;
231    }
232}
233
234impl Sub<&ValueCommitment> for ValueCommitment {
235    type Output = CommitmentSum;
236
237    fn sub(self, rhs: &Self) -> Self::Output {
238        CommitmentSum(self.0 - rhs.0)
239    }
240}
241
242impl SubAssign<&ValueCommitment> for CommitmentSum {
243    fn sub_assign(&mut self, rhs: &ValueCommitment) {
244        self.0 -= rhs.0;
245    }
246}
247
248impl Sub<CommitmentSum> for CommitmentSum {
249    type Output = CommitmentSum;
250
251    fn sub(self, rhs: Self) -> Self::Output {
252        CommitmentSum(self.0 - rhs.0)
253    }
254}
255
256impl Sum<ValueCommitment> for CommitmentSum {
257    fn sum<I: Iterator<Item = ValueCommitment>>(iter: I) -> Self {
258        iter.fold(CommitmentSum::zero(), |acc, cv| acc + &cv)
259    }
260}
261
262impl<'a> Sum<&'a ValueCommitment> for CommitmentSum {
263    fn sum<I: Iterator<Item = &'a ValueCommitment>>(iter: I) -> Self {
264        iter.fold(CommitmentSum::zero(), |acc, cv| acc + cv)
265    }
266}