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

View File

@ -41,7 +41,7 @@ use crate::transaction::components::transparent::TxOut;
#[cfg(feature = "zfuture")]
use crate::{
extensions::transparent::{AuthData, ExtensionTxBuilder, ToPayload},
extensions::transparent::{ExtensionTxBuilder, ToPayload},
transaction::components::{
tze::builder::TzeBuilder,
tze::{self, TzeOut},
@ -333,7 +333,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
.map_err(Error::SaplingBuild)?;
#[cfg(feature = "zfuture")]
let tze_bundle = self.tze_builder.build();
let (tze_bundle, tze_signers) = self.tze_builder.build();
let unauthed_tx = TransactionData {
version,
@ -384,9 +384,11 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
.map_err(Error::SaplingBuild)?;
#[cfg(feature = "zfuture")]
let tze_witnesses = self
.tze_builder
.create_witnesses(&unauthed_tx)
let tze_bundle = unauthed_tx
.tze_bundle
.clone()
.map(|b| b.into_authorized(&unauthed_tx, tze_signers))
.transpose()
.map_err(Error::TzeBuild)?;
Ok((
@ -395,7 +397,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
sapling_sigs,
transparent_bundle,
#[cfg(feature = "zfuture")]
tze_witnesses,
tze_bundle,
)
.expect("An IO error occurred applying signatures."),
tx_metadata,
@ -406,7 +408,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
unauthed_tx: TransactionData<Unauthorized>,
sapling_sigs: Option<(Vec<redjubjub::Signature>, redjubjub::Signature)>,
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> {
let signed_sapling_bundle = match (unauthed_tx.sapling_bundle, sapling_sigs) {
(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 {
version: unauthed_tx.version,
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,
orchard_bundle: None,
#[cfg(feature = "zfuture")]
tze_bundle: signed_tze_bundle,
tze_bundle,
};
authorized_tx.freeze()

View File

@ -25,13 +25,6 @@ pub trait Authorization: Debug {
type Witness: Debug + Clone + PartialEq;
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Unauthorized;
impl Authorization for Unauthorized {
type Witness = ();
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Authorized;
@ -41,30 +34,9 @@ impl Authorization for Authorized {
#[derive(Debug, Clone, PartialEq)]
pub struct Bundle<A: Authorization> {
pub vin: Vec<TzeIn<A>>,
pub vin: Vec<TzeIn<A::Witness>>,
pub vout: Vec<TzeOut>,
}
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,
}
}
pub authorization: A,
}
#[derive(Clone, Debug, PartialEq)]
@ -99,12 +71,12 @@ impl OutPoint {
}
#[derive(Clone, Debug, PartialEq)]
pub struct TzeIn<A: Authorization> {
pub struct TzeIn<Payload> {
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)
///
/// 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].
///
/// [ZIP 222]: https://zips.z.cash/zip-0222#encoding-in-transactions
impl TzeIn<Unauthorized> {
impl TzeIn<()> {
/// Convenience constructor
pub fn new(prevout: OutPoint, extension_id: u32, mode: u32) -> Self {
TzeIn {
@ -141,7 +113,7 @@ impl TzeIn<Unauthorized> {
}
}
impl TzeIn<Authorized> {
impl TzeIn<<Authorized as Authorization>::Witness> {
/// Read witness metadata & payload
///
/// Used to decode the encoded form used within a serialized
@ -249,7 +221,7 @@ pub mod testing {
}
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 }
}
}
@ -274,7 +246,7 @@ pub mod testing {
if branch_id != BranchId::ZFuture || (vin.is_empty() && vout.is_empty()) {
None
} else {
Some(Bundle { vin, vout })
Some(Bundle { vin, vout, authorization: Authorized })
}
}
}

View File

@ -5,9 +5,12 @@ use std::fmt;
use crate::{
extensions::transparent::{self as tze, ToPayload},
transaction::components::{
amount::Amount,
tze::{Bundle, OutPoint, TzeIn, TzeOut, Unauthorized},
transaction::{
self as tx,
components::{
amount::Amount,
tze::{Authorization, Authorized, Bundle, OutPoint, TzeIn, TzeOut},
},
},
};
@ -28,17 +31,24 @@ impl fmt::Display for Error {
}
#[allow(clippy::type_complexity)]
struct TzeSigner<'a, BuildCtx> {
pub struct TzeSigner<'a, BuildCtx> {
prevout: TzeOut,
builder: Box<dyn FnOnce(&BuildCtx) -> Result<(u32, Vec<u8>), Error> + 'a>,
}
pub struct TzeBuilder<'a, BuildCtx> {
signers: Vec<TzeSigner<'a, BuildCtx>>,
vin: Vec<TzeIn<Unauthorized>>,
vin: Vec<TzeIn<()>>,
vout: Vec<TzeOut>,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Unauthorized;
impl Authorization for Unauthorized {
type Witness = ();
}
impl<'a, BuildCtx> TzeBuilder<'a, BuildCtx> {
pub fn empty() -> Self {
TzeBuilder {
@ -99,43 +109,63 @@ impl<'a, BuildCtx> TzeBuilder<'a, BuildCtx> {
.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() {
None
(None, vec![])
} else {
Some(Bundle {
vin: self.vin.clone(),
vout: self.vout.clone(),
})
}
}
pub fn create_witnesses(self, mtx: &BuildCtx) -> Result<Option<Vec<tze::AuthData>>, Error> {
// Create TZE input witnesses
if self.vin.is_empty() && self.vout.is_empty() {
Ok(None)
} else {
// Create TZE input witnesses
let payloads = self
.signers
.into_iter()
.zip(self.vin.into_iter())
.into_iter()
.map(|(signer, tzein)| {
// 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
// transaction builder; here, it then computes those commitments.
let (mode, payload) = (signer.builder)(&mtx)?;
let input_mode = tzein.witness.mode;
if mode != input_mode {
return Err(Error::WitnessModeMismatch(input_mode, mode));
}
Ok(tze::AuthData(payload))
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(Some(payloads))
(
Some(Bundle {
vin: self.vin.clone(),
vout: self.vout.clone(),
authorization: Unauthorized,
}),
self.signers,
)
}
}
}
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
let payloads = signers
.into_iter()
.zip(self.vin.iter())
.into_iter()
.map(|(signer, tzein)| {
// 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
// transaction builder; here, it then computes those commitments.
let (mode, payload) = (signer.builder)(unauthed_tx)?;
let input_mode = tzein.witness.mode;
if mode != input_mode {
return Err(Error::WitnessModeMismatch(input_mode, mode));
}
Ok(tze::AuthData(payload))
})
.collect::<Result<Vec<_>, Error>>()?;
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;
#[cfg(feature = "zfuture")]
type TzeAuth = tze::Unauthorized;
type TzeAuth = tze::builder::Unauthorized;
}
/// A Zcash transaction.
@ -795,7 +795,11 @@ impl Transaction {
Ok(if vin.is_empty() && vout.is_empty() {
None
} 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
/// hash from just the personalization string.
#[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);
for tzein in tze_inputs {
tzein.write_without_witness(&mut h).unwrap();