Clean up TZE signature generation.
This commit is contained in:
parent
ab1b31ebf6
commit
6635895e55
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::{
|
||||||
amount::Amount,
|
self as tx,
|
||||||
tze::{Bundle, OutPoint, TzeIn, TzeOut, Unauthorized},
|
components::{
|
||||||
|
amount::Amount,
|
||||||
|
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,43 +109,63 @@ 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 {
|
(
|
||||||
vin: self.vin.clone(),
|
Some(Bundle {
|
||||||
vout: self.vout.clone(),
|
vin: self.vin.clone(),
|
||||||
})
|
vout: self.vout.clone(),
|
||||||
}
|
authorization: Unauthorized,
|
||||||
}
|
}),
|
||||||
|
self.signers,
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue