sapling_crypto/value/
sums.rs1use 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#[derive(Debug)]
14#[non_exhaustive]
15pub enum BalanceError {
16 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
43pub struct ValueSum(i128);
44
45impl ValueSum {
46 pub fn zero() -> Self {
48 ValueSum(0)
49 }
50
51 #[cfg(feature = "wallet")]
55 pub(crate) fn from_raw(value_sum: i128) -> Self {
56 Self(value_sum)
57 }
58
59 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#[derive(Clone, Copy, Debug)]
109pub struct TrapdoorSum(jubjub::Scalar);
110
111impl TrapdoorSum {
112 pub fn zero() -> Self {
114 TrapdoorSum(jubjub::Scalar::zero())
115 }
116
117 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#[derive(Clone, Copy, Debug)]
178pub struct CommitmentSum(jubjub::ExtendedPoint);
179
180impl CommitmentSum {
181 pub fn zero() -> Self {
183 CommitmentSum(jubjub::ExtendedPoint::identity())
184 }
185
186 pub fn into_bvk<V: Into<i64>>(self, value_balance: V) -> redjubjub::VerificationKey<Binding> {
190 let value: i64 = value_balance.into();
191
192 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 let value_balance = if value.is_negative() {
200 -jubjub::Scalar::from(abs_value)
201 } else {
202 jubjub::Scalar::from(abs_value)
203 };
204
205 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}