sapling_crypto/pczt/
io_finalizer.rs1use core::fmt;
2
3use alloc::vec::Vec;
4use rand::{CryptoRng, RngCore};
5
6use crate::value::{CommitmentSum, TrapdoorSum};
7
8use super::SignerError;
9
10impl super::Bundle {
11 pub fn finalize_io<R: RngCore + CryptoRng>(
13 &mut self,
14 sighash: [u8; 32],
15 mut rng: R,
16 ) -> Result<(), IoFinalizerError> {
17 let bsk = {
19 let spend_rcvs = self
20 .spends
21 .iter()
22 .map(|spend| {
23 spend
24 .rcv
25 .as_ref()
26 .ok_or(IoFinalizerError::MissingValueCommitTrapdoor)
27 })
28 .collect::<Result<Vec<_>, _>>()?;
29
30 let output_rcvs = self
31 .outputs
32 .iter()
33 .map(|output| {
34 output
35 .rcv
36 .as_ref()
37 .ok_or(IoFinalizerError::MissingValueCommitTrapdoor)
38 })
39 .collect::<Result<Vec<_>, _>>()?;
40
41 let spends: TrapdoorSum = spend_rcvs.into_iter().sum();
42 let outputs: TrapdoorSum = output_rcvs.into_iter().sum();
43 (spends - outputs).into_bsk()
44 };
45
46 let bvk = {
48 let spends = self
49 .spends
50 .iter()
51 .map(|spend| spend.cv())
52 .sum::<CommitmentSum>();
53 let outputs = self
54 .outputs
55 .iter()
56 .map(|output| output.cv())
57 .sum::<CommitmentSum>();
58 (spends - outputs).into_bvk(
59 i64::try_from(self.value_sum).map_err(|_| IoFinalizerError::InvalidValueSum)?,
60 )
61 };
62 if redjubjub::VerificationKey::from(&bsk) != bvk {
63 return Err(IoFinalizerError::ValueCommitMismatch);
64 }
65 self.bsk = Some(bsk);
66
67 for spend in self.spends.iter_mut() {
69 if let Some(ask) = spend.dummy_ask.take() {
72 spend
73 .sign(sighash, &ask, &mut rng)
74 .map_err(IoFinalizerError::DummySignature)?;
75 }
76 }
77
78 Ok(())
79 }
80}
81
82#[derive(Debug)]
84#[non_exhaustive]
85pub enum IoFinalizerError {
86 DummySignature(SignerError),
88 InvalidValueSum,
90 MissingValueCommitTrapdoor,
92 ValueCommitMismatch,
95}
96
97impl fmt::Display for IoFinalizerError {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 match self {
100 IoFinalizerError::DummySignature(e) => {
101 write!(f, "an error occurred while signing a dummy spend: {e}")
102 }
103 IoFinalizerError::InvalidValueSum => {
104 write!(f, "the `value_sum` field is too large for a `valueBalance`")
105 }
106 IoFinalizerError::MissingValueCommitTrapdoor => write!(
107 f,
108 "the IO Finalizer role requires all `rcv` fields to be set"
109 ),
110 IoFinalizerError::ValueCommitMismatch => write!(
111 f,
112 "`cv`, `rcv`, and `value_sum` within the Sapling bundle are inconsistent"
113 ),
114 }
115 }
116}
117
118#[cfg(feature = "std")]
119impl std::error::Error for IoFinalizerError {}