Clean up TZE signature generation.
This commit is contained in:
parent
ab1b31ebf6
commit
6635895e55
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue