Clean up TZE signature generation.

This commit is contained in:
Kris Nuttycombe 2021-06-03 18:25:41 -06:00
parent ab1b31ebf6
commit 6635895e55
6 changed files with 100 additions and 100 deletions

View File

@ -495,7 +495,7 @@ mod tests {
builder::Builder, builder::Builder,
components::{ components::{
amount::{Amount, DEFAULT_FEE}, amount::{Amount, DEFAULT_FEE},
tze::{Bundle, OutPoint, TzeIn, TzeOut}, tze::{Authorized, Bundle, OutPoint, TzeIn, TzeOut},
}, },
Transaction, TransactionData, TxVersion, Transaction, TransactionData, TxVersion,
}, },
@ -695,6 +695,7 @@ mod tests {
Some(Bundle { Some(Bundle {
vin: vec![], vin: vec![],
vout: vec![out_a], vout: vec![out_a],
authorization: Authorized,
}), }),
) )
.freeze() .freeze()
@ -725,6 +726,7 @@ mod tests {
Some(Bundle { Some(Bundle {
vin: vec![in_b], vin: vec![in_b],
vout: vec![out_b], vout: vec![out_b],
authorization: Authorized,
}), }),
) )
.freeze() .freeze()
@ -751,6 +753,7 @@ mod tests {
Some(Bundle { Some(Bundle {
vin: vec![in_c], vin: vec![in_c],
vout: vec![], vout: vec![],
authorization: Authorized,
}), }),
) )
.freeze() .freeze()

View File

@ -41,7 +41,7 @@ use crate::transaction::components::transparent::TxOut;
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
use crate::{ use crate::{
extensions::transparent::{AuthData, ExtensionTxBuilder, ToPayload}, extensions::transparent::{ExtensionTxBuilder, ToPayload},
transaction::components::{ transaction::components::{
tze::builder::TzeBuilder, tze::builder::TzeBuilder,
tze::{self, TzeOut}, tze::{self, TzeOut},
@ -333,7 +333,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
.map_err(Error::SaplingBuild)?; .map_err(Error::SaplingBuild)?;
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
let tze_bundle = self.tze_builder.build(); let (tze_bundle, tze_signers) = self.tze_builder.build();
let unauthed_tx = TransactionData { let unauthed_tx = TransactionData {
version, version,
@ -384,9 +384,11 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
.map_err(Error::SaplingBuild)?; .map_err(Error::SaplingBuild)?;
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
let tze_witnesses = self let tze_bundle = unauthed_tx
.tze_builder .tze_bundle
.create_witnesses(&unauthed_tx) .clone()
.map(|b| b.into_authorized(&unauthed_tx, tze_signers))
.transpose()
.map_err(Error::TzeBuild)?; .map_err(Error::TzeBuild)?;
Ok(( Ok((
@ -395,7 +397,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
sapling_sigs, sapling_sigs,
transparent_bundle, transparent_bundle,
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
tze_witnesses, tze_bundle,
) )
.expect("An IO error occurred applying signatures."), .expect("An IO error occurred applying signatures."),
tx_metadata, tx_metadata,
@ -406,7 +408,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
unauthed_tx: TransactionData<Unauthorized>, unauthed_tx: TransactionData<Unauthorized>,
sapling_sigs: Option<(Vec<redjubjub::Signature>, redjubjub::Signature)>, sapling_sigs: Option<(Vec<redjubjub::Signature>, redjubjub::Signature)>,
transparent_bundle: Option<transparent::Bundle<transparent::Authorized>>, transparent_bundle: Option<transparent::Bundle<transparent::Authorized>>,
#[cfg(feature = "zfuture")] tze_witnesses: Option<Vec<AuthData>>, #[cfg(feature = "zfuture")] tze_bundle: Option<tze::Bundle<tze::Authorized>>,
) -> io::Result<Transaction> { ) -> io::Result<Transaction> {
let signed_sapling_bundle = match (unauthed_tx.sapling_bundle, sapling_sigs) { let signed_sapling_bundle = match (unauthed_tx.sapling_bundle, sapling_sigs) {
(Some(bundle), Some((spend_sigs, binding_sig))) => { (Some(bundle), Some((spend_sigs, binding_sig))) => {
@ -418,17 +420,6 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
} }
}; };
#[cfg(feature = "zfuture")]
let signed_tze_bundle = match (unauthed_tx.tze_bundle, tze_witnesses) {
(Some(bundle), Some(witness_payloads)) => {
Some(bundle.apply_signatures(witness_payloads))
}
(None, None) => None,
_ => {
panic!("Mismatch between TZE bundle and witnesses.")
}
};
let authorized_tx = TransactionData { let authorized_tx = TransactionData {
version: unauthed_tx.version, version: unauthed_tx.version,
consensus_branch_id: unauthed_tx.consensus_branch_id, consensus_branch_id: unauthed_tx.consensus_branch_id,
@ -439,7 +430,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
sapling_bundle: signed_sapling_bundle, sapling_bundle: signed_sapling_bundle,
orchard_bundle: None, orchard_bundle: None,
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
tze_bundle: signed_tze_bundle, tze_bundle,
}; };
authorized_tx.freeze() authorized_tx.freeze()

View File

@ -25,13 +25,6 @@ pub trait Authorization: Debug {
type Witness: Debug + Clone + PartialEq; type Witness: Debug + Clone + PartialEq;
} }
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Unauthorized;
impl Authorization for Unauthorized {
type Witness = ();
}
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct Authorized; pub struct Authorized;
@ -41,30 +34,9 @@ impl Authorization for Authorized {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Bundle<A: Authorization> { pub struct Bundle<A: Authorization> {
pub vin: Vec<TzeIn<A>>, pub vin: Vec<TzeIn<A::Witness>>,
pub vout: Vec<TzeOut>, pub vout: Vec<TzeOut>,
} pub authorization: A,
impl Bundle<Unauthorized> {
pub fn apply_signatures(self, witnesses: Vec<tze::AuthData>) -> Bundle<Authorized> {
assert!(self.vin.len() == witnesses.len());
Bundle {
vin: self
.vin
.into_iter()
.zip(witnesses.into_iter())
.map(|(tzein, payload)| TzeIn {
prevout: tzein.prevout,
witness: tze::Witness {
extension_id: tzein.witness.extension_id,
mode: tzein.witness.mode,
payload,
},
})
.collect(),
vout: self.vout,
}
}
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -99,12 +71,12 @@ impl OutPoint {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct TzeIn<A: Authorization> { pub struct TzeIn<Payload> {
pub prevout: OutPoint, pub prevout: OutPoint,
pub witness: tze::Witness<A::Witness>, pub witness: tze::Witness<Payload>,
} }
impl<A: Authorization> TzeIn<A> { impl<Payload> TzeIn<Payload> {
/// Write without witness data (for signature hashing) /// Write without witness data (for signature hashing)
/// ///
/// This is also used as the prefix for the encoded form used /// This is also used as the prefix for the encoded form used
@ -127,7 +99,7 @@ impl<A: Authorization> TzeIn<A> {
/// Transaction encoding and decoding functions conforming to [ZIP 222]. /// Transaction encoding and decoding functions conforming to [ZIP 222].
/// ///
/// [ZIP 222]: https://zips.z.cash/zip-0222#encoding-in-transactions /// [ZIP 222]: https://zips.z.cash/zip-0222#encoding-in-transactions
impl TzeIn<Unauthorized> { impl TzeIn<()> {
/// Convenience constructor /// Convenience constructor
pub fn new(prevout: OutPoint, extension_id: u32, mode: u32) -> Self { pub fn new(prevout: OutPoint, extension_id: u32, mode: u32) -> Self {
TzeIn { TzeIn {
@ -141,7 +113,7 @@ impl TzeIn<Unauthorized> {
} }
} }
impl TzeIn<Authorized> { impl TzeIn<<Authorized as Authorization>::Witness> {
/// Read witness metadata & payload /// Read witness metadata & payload
/// ///
/// Used to decode the encoded form used within a serialized /// Used to decode the encoded form used within a serialized
@ -249,7 +221,7 @@ pub mod testing {
} }
prop_compose! { prop_compose! {
pub fn arb_tzein()(prevout in arb_outpoint(), witness in arb_witness()) -> TzeIn<Authorized> { pub fn arb_tzein()(prevout in arb_outpoint(), witness in arb_witness()) -> TzeIn<AuthData> {
TzeIn { prevout, witness } TzeIn { prevout, witness }
} }
} }
@ -274,7 +246,7 @@ pub mod testing {
if branch_id != BranchId::ZFuture || (vin.is_empty() && vout.is_empty()) { if branch_id != BranchId::ZFuture || (vin.is_empty() && vout.is_empty()) {
None None
} else { } else {
Some(Bundle { vin, vout }) Some(Bundle { vin, vout, authorization: Authorized })
} }
} }
} }

View File

@ -5,9 +5,12 @@ use std::fmt;
use crate::{ use crate::{
extensions::transparent::{self as tze, ToPayload}, extensions::transparent::{self as tze, ToPayload},
transaction::components::{ transaction::{
self as tx,
components::{
amount::Amount, amount::Amount,
tze::{Bundle, OutPoint, TzeIn, TzeOut, Unauthorized}, tze::{Authorization, Authorized, Bundle, OutPoint, TzeIn, TzeOut},
},
}, },
}; };
@ -28,17 +31,24 @@ impl fmt::Display for Error {
} }
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
struct TzeSigner<'a, BuildCtx> { pub struct TzeSigner<'a, BuildCtx> {
prevout: TzeOut, prevout: TzeOut,
builder: Box<dyn FnOnce(&BuildCtx) -> Result<(u32, Vec<u8>), Error> + 'a>, builder: Box<dyn FnOnce(&BuildCtx) -> Result<(u32, Vec<u8>), Error> + 'a>,
} }
pub struct TzeBuilder<'a, BuildCtx> { pub struct TzeBuilder<'a, BuildCtx> {
signers: Vec<TzeSigner<'a, BuildCtx>>, signers: Vec<TzeSigner<'a, BuildCtx>>,
vin: Vec<TzeIn<Unauthorized>>, vin: Vec<TzeIn<()>>,
vout: Vec<TzeOut>, vout: Vec<TzeOut>,
} }
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Unauthorized;
impl Authorization for Unauthorized {
type Witness = ();
}
impl<'a, BuildCtx> TzeBuilder<'a, BuildCtx> { impl<'a, BuildCtx> TzeBuilder<'a, BuildCtx> {
pub fn empty() -> Self { pub fn empty() -> Self {
TzeBuilder { TzeBuilder {
@ -99,33 +109,38 @@ impl<'a, BuildCtx> TzeBuilder<'a, BuildCtx> {
.sum::<Option<Amount>>()? .sum::<Option<Amount>>()?
} }
pub fn build(&self) -> Option<Bundle<Unauthorized>> { pub fn build(self) -> (Option<Bundle<Unauthorized>>, Vec<TzeSigner<'a, BuildCtx>>) {
if self.vin.is_empty() && self.vout.is_empty() { if self.vin.is_empty() && self.vout.is_empty() {
None (None, vec![])
} else { } else {
(
Some(Bundle { Some(Bundle {
vin: self.vin.clone(), vin: self.vin.clone(),
vout: self.vout.clone(), vout: self.vout.clone(),
}) authorization: Unauthorized,
}),
self.signers,
)
} }
} }
}
pub fn create_witnesses(self, mtx: &BuildCtx) -> Result<Option<Vec<tze::AuthData>>, Error> { impl Bundle<Unauthorized> {
pub fn into_authorized(
self,
unauthed_tx: &tx::TransactionData<tx::Unauthorized>,
signers: Vec<TzeSigner<'_, tx::TransactionData<tx::Unauthorized>>>,
) -> Result<Bundle<Authorized>, Error> {
// Create TZE input witnesses // Create TZE input witnesses
if self.vin.is_empty() && self.vout.is_empty() { let payloads = signers
Ok(None)
} else {
// Create TZE input witnesses
let payloads = self
.signers
.into_iter() .into_iter()
.zip(self.vin.into_iter()) .zip(self.vin.iter())
.into_iter() .into_iter()
.map(|(signer, tzein)| { .map(|(signer, tzein)| {
// The witness builder function should have cached/closed over whatever data was // The witness builder function should have cached/closed over whatever data was
// necessary for the witness to commit to at the time it was added to the // necessary for the witness to commit to at the time it was added to the
// transaction builder; here, it then computes those commitments. // transaction builder; here, it then computes those commitments.
let (mode, payload) = (signer.builder)(&mtx)?; let (mode, payload) = (signer.builder)(unauthed_tx)?;
let input_mode = tzein.witness.mode; let input_mode = tzein.witness.mode;
if mode != input_mode { if mode != input_mode {
return Err(Error::WitnessModeMismatch(input_mode, mode)); return Err(Error::WitnessModeMismatch(input_mode, mode));
@ -135,7 +150,22 @@ impl<'a, BuildCtx> TzeBuilder<'a, BuildCtx> {
}) })
.collect::<Result<Vec<_>, Error>>()?; .collect::<Result<Vec<_>, Error>>()?;
Ok(Some(payloads)) Ok(Bundle {
} vin: self
.vin
.into_iter()
.zip(payloads.into_iter())
.map(|(tzein, payload)| TzeIn {
prevout: tzein.prevout,
witness: tze::Witness {
extension_id: tzein.witness.extension_id,
mode: tzein.witness.mode,
payload,
},
})
.collect(),
vout: self.vout,
authorization: Authorized,
})
} }
} }

View File

@ -261,7 +261,7 @@ impl Authorization for Unauthorized {
type OrchardAuth = orchard::builder::Unauthorized; type OrchardAuth = orchard::builder::Unauthorized;
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
type TzeAuth = tze::Unauthorized; type TzeAuth = tze::builder::Unauthorized;
} }
/// A Zcash transaction. /// A Zcash transaction.
@ -795,7 +795,11 @@ impl Transaction {
Ok(if vin.is_empty() && vout.is_empty() { Ok(if vin.is_empty() && vout.is_empty() {
None None
} else { } else {
Some(tze::Bundle { vin, vout }) Some(tze::Bundle {
vin,
vout,
authorization: tze::Authorized,
})
}) })
} }

View File

@ -116,7 +116,7 @@ pub(crate) fn transparent_outputs_hash<T: Borrow<TxOut>>(vout: &[T]) -> Blake2bH
/// In the case that no inputs are provided, this produces a default /// In the case that no inputs are provided, this produces a default
/// hash from just the personalization string. /// hash from just the personalization string.
#[cfg(feature = "zfuture")] #[cfg(feature = "zfuture")]
pub(crate) fn hash_tze_inputs<A: tze::Authorization>(tze_inputs: &[TzeIn<A>]) -> Blake2bHash { pub(crate) fn hash_tze_inputs<A>(tze_inputs: &[TzeIn<A>]) -> Blake2bHash {
let mut h = hasher(ZCASH_TZE_INPUTS_HASH_PERSONALIZATION); let mut h = hasher(ZCASH_TZE_INPUTS_HASH_PERSONALIZATION);
for tzein in tze_inputs { for tzein in tze_inputs {
tzein.write_without_witness(&mut h).unwrap(); tzein.write_without_witness(&mut h).unwrap();