use core::fmt;
use alloc::vec::Vec;
use rand::{CryptoRng, RngCore};
use crate::{
keys::SpendAuthorizingKey,
primitives::redpallas,
value::{ValueCommitTrapdoor, ValueCommitment},
};
use super::SignerError;
impl super::Bundle {
pub fn finalize_io<R: RngCore + CryptoRng>(
&mut self,
sighash: [u8; 32],
mut rng: R,
) -> Result<(), IoFinalizerError> {
let rcvs = self
.actions
.iter()
.map(|a| {
a.rcv
.as_ref()
.ok_or(IoFinalizerError::MissingValueCommitTrapdoor)
})
.collect::<Result<Vec<_>, _>>()?;
let bsk = rcvs.into_iter().sum::<ValueCommitTrapdoor>().into_bsk();
let bvk = (self
.actions
.iter()
.map(|a| a.cv_net())
.sum::<ValueCommitment>()
- ValueCommitment::derive(self.value_sum, ValueCommitTrapdoor::zero()))
.into_bvk();
if redpallas::VerificationKey::from(&bsk) != bvk {
return Err(IoFinalizerError::ValueCommitMismatch);
}
self.bsk = Some(bsk);
for action in self.actions.iter_mut() {
if let Some(sk) = action.spend.dummy_sk.take() {
let ask = SpendAuthorizingKey::from(&sk);
action
.sign(sighash, &ask, &mut rng)
.map_err(IoFinalizerError::DummySignature)?;
}
}
Ok(())
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum IoFinalizerError {
DummySignature(SignerError),
MissingValueCommitTrapdoor,
ValueCommitMismatch,
}
impl fmt::Display for IoFinalizerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IoFinalizerError::DummySignature(e) => {
write!(f, "An error occurred while signing a dummy spend: {e}")
}
IoFinalizerError::MissingValueCommitTrapdoor => write!(
f,
"The IO Finalizer role requires all `rcv` fields to be set"
),
IoFinalizerError::ValueCommitMismatch => write!(
f,
"`cv_net`, `rcv`, and `value_sum` within the Orchard bundle are inconsistent."
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for IoFinalizerError {}