1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use alloc::vec::Vec;
use rand::{CryptoRng, RngCore};
use crate::value::{CommitmentSum, TrapdoorSum};
use super::SignerError;
impl super::Bundle {
/// Finalizes the IO for this bundle.
pub fn finalize_io<R: RngCore + CryptoRng>(
&mut self,
sighash: [u8; 32],
mut rng: R,
) -> Result<(), IoFinalizerError> {
// Compute the transaction binding signing key.
let bsk = {
let spend_rcvs = self
.spends
.iter()
.map(|spend| {
spend
.rcv
.as_ref()
.ok_or(IoFinalizerError::MissingValueCommitTrapdoor)
})
.collect::<Result<Vec<_>, _>>()?;
let output_rcvs = self
.outputs
.iter()
.map(|output| {
output
.rcv
.as_ref()
.ok_or(IoFinalizerError::MissingValueCommitTrapdoor)
})
.collect::<Result<Vec<_>, _>>()?;
let spends: TrapdoorSum = spend_rcvs.into_iter().sum();
let outputs: TrapdoorSum = output_rcvs.into_iter().sum();
(spends - outputs).into_bsk()
};
// Verify that bsk and bvk are consistent.
let bvk = {
let spends = self
.spends
.iter()
.map(|spend| spend.cv())
.sum::<CommitmentSum>();
let outputs = self
.outputs
.iter()
.map(|output| output.cv())
.sum::<CommitmentSum>();
(spends - outputs).into_bvk(
i64::try_from(self.value_sum).map_err(|_| IoFinalizerError::InvalidValueSum)?,
)
};
if redjubjub::VerificationKey::from(&bsk) != bvk {
return Err(IoFinalizerError::ValueCommitMismatch);
}
self.bsk = Some(bsk);
// Add signatures to dummy spends.
for spend in self.spends.iter_mut() {
// The `Option::take` ensures we don't have any spend authorizing keys in the
// PCZT after the IO Finalizer has run.
if let Some(ask) = spend.dummy_ask.take() {
spend
.sign(sighash, &ask, &mut rng)
.map_err(IoFinalizerError::DummySignature)?;
}
}
Ok(())
}
}
/// Errors that can occur while finalizing the I/O for a PCZT bundle.
#[derive(Debug)]
pub enum IoFinalizerError {
/// An error occurred while signing a dummy spend.
DummySignature(SignerError),
/// The `value_sum` is too large for the `value_balance` field.
InvalidValueSum,
/// The IO Finalizer role requires all `rcv` fields to be set.
MissingValueCommitTrapdoor,
/// The `cv_net`, `rcv`, and `value_sum` values within the Orchard bundle are
/// inconsistent.
ValueCommitMismatch,
}