Store partial authorizing data for transparent txs in transparent authorization.

This commit is contained in:
Kris Nuttycombe 2021-06-03 17:27:15 -06:00
parent dac68ce2aa
commit ab1b31ebf6
4 changed files with 105 additions and 106 deletions

View File

@ -37,7 +37,7 @@ use crate::{
};
#[cfg(feature = "transparent-inputs")]
use crate::{legacy::Script, transaction::components::transparent::TxOut};
use crate::transaction::components::transparent::TxOut;
#[cfg(feature = "zfuture")]
use crate::{
@ -353,10 +353,14 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
//
let txid_parts = unauthed_tx.digest(TxIdDigester);
#[cfg(feature = "transparent-inputs")]
let transparent_sigs = self
.transparent_builder
.create_signatures(&unauthed_tx, &txid_parts);
let transparent_bundle = unauthed_tx.transparent_bundle.clone().map(|b| {
b.apply_signatures(
#[cfg(feature = "transparent-inputs")]
&unauthed_tx,
#[cfg(feature = "transparent-inputs")]
&txid_parts,
)
});
// the commitment being signed is shared across all Sapling inputs; once
// V4 transactions are deprecated this should just be the txid, but
@ -389,8 +393,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
Self::apply_signatures(
unauthed_tx,
sapling_sigs,
#[cfg(feature = "transparent-inputs")]
transparent_sigs,
transparent_bundle,
#[cfg(feature = "zfuture")]
tze_witnesses,
)
@ -402,23 +405,9 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
pub fn apply_signatures(
unauthed_tx: TransactionData<Unauthorized>,
sapling_sigs: Option<(Vec<redjubjub::Signature>, redjubjub::Signature)>,
#[cfg(feature = "transparent-inputs")] transparent_sigs: Option<Vec<Script>>,
transparent_bundle: Option<transparent::Bundle<transparent::Authorized>>,
#[cfg(feature = "zfuture")] tze_witnesses: Option<Vec<AuthData>>,
) -> io::Result<Transaction> {
#[cfg(feature = "transparent-inputs")]
let transparent_bundle = match (unauthed_tx.transparent_bundle, transparent_sigs) {
(Some(bundle), Some(script_sigs)) => Some(bundle.apply_signatures(script_sigs)),
(None, None) => None,
_ => {
panic!("Mismatch between transparent bundle and signatures.");
}
};
#[cfg(not(feature = "transparent-inputs"))]
let transparent_bundle = unauthed_tx
.transparent_bundle
.map(|b| b.apply_signatures(vec![]));
let signed_sapling_bundle = match (unauthed_tx.sapling_bundle, sapling_sigs) {
(Some(bundle), Some((spend_sigs, binding_sig))) => {
Some(bundle.apply_signatures(spend_sigs, binding_sig))

View File

@ -15,13 +15,6 @@ pub trait Authorization: Debug {
type ScriptSig: Debug + Clone + PartialEq;
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Unauthorized;
impl Authorization for Unauthorized {
type ScriptSig = ();
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Authorized;
@ -33,25 +26,7 @@ impl Authorization for Authorized {
pub struct Bundle<A: Authorization> {
pub vin: Vec<TxIn<A>>,
pub vout: Vec<TxOut>,
}
impl Bundle<Unauthorized> {
pub fn apply_signatures(self, script_sigs: Vec<Script>) -> Bundle<Authorized> {
assert!(self.vin.len() == script_sigs.len());
Bundle {
vin: self
.vin
.into_iter()
.zip(script_sigs.into_iter())
.map(|(txin, sig)| TxIn {
prevout: txin.prevout,
script_sig: sig,
sequence: txin.sequence,
})
.collect(),
vout: self.vout,
}
}
pub authorization: A,
}
#[derive(Clone, Debug, PartialEq)]
@ -93,18 +68,6 @@ pub struct TxIn<A: Authorization> {
pub sequence: u32,
}
impl TxIn<Unauthorized> {
#[cfg(feature = "transparent-inputs")]
#[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))]
pub fn new(prevout: OutPoint) -> Self {
TxIn {
prevout,
script_sig: (),
sequence: std::u32::MAX,
}
}
}
impl TxIn<Authorized> {
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
let prevout = OutPoint::read(&mut reader)?;
@ -210,7 +173,7 @@ pub mod testing {
if vin.is_empty() && vout.is_empty() {
None
} else {
Some(Bundle { vin, vout })
Some(Bundle { vin, vout, authorization: Authorized })
}
}
}

View File

@ -9,7 +9,7 @@ use crate::{
legacy::TransparentAddress,
transaction::components::{
amount::Amount,
transparent::{self, TxIn, TxOut},
transparent::{self, Authorization, Authorized, Bundle, TxIn, TxOut},
},
};
@ -17,9 +17,10 @@ use crate::{
use crate::{
legacy::Script,
transaction::{
self as tx,
components::OutPoint,
sighash::{signature_hash, SignableInput, SIGHASH_ALL},
TransactionData, TxDigests, Unauthorized,
TransactionData, TxDigests,
},
};
@ -39,6 +40,7 @@ impl fmt::Display for Error {
}
#[cfg(feature = "transparent-inputs")]
#[derive(Debug, Clone)]
struct TransparentInputInfo {
sk: secp256k1::SecretKey,
pubkey: [u8; secp256k1::constants::PUBLIC_KEY_SIZE],
@ -54,6 +56,18 @@ pub struct TransparentBuilder {
vout: Vec<TxOut>,
}
#[derive(Debug, Clone)]
pub struct Unauthorized {
#[cfg(feature = "transparent-inputs")]
secp: secp256k1::Secp256k1<secp256k1::SignOnly>,
#[cfg(feature = "transparent-inputs")]
inputs: Vec<TransparentInputInfo>,
}
impl Authorization for Unauthorized {
type ScriptSig = ();
}
impl TransparentBuilder {
pub fn empty() -> Self {
TransparentBuilder {
@ -134,65 +148,94 @@ impl TransparentBuilder {
.sum::<Option<Amount>>()?
}
pub fn build(&self) -> Option<transparent::Bundle<transparent::Unauthorized>> {
pub fn build(self) -> Option<transparent::Bundle<Unauthorized>> {
#[cfg(feature = "transparent-inputs")]
let vin: Vec<TxIn<transparent::Unauthorized>> = self
let vin: Vec<TxIn<Unauthorized>> = self
.inputs
.iter()
.map(|i| TxIn::new(i.utxo.clone()))
.collect();
#[cfg(not(feature = "transparent-inputs"))]
let vin: Vec<TxIn<transparent::Unauthorized>> = vec![];
let vin: Vec<TxIn<Unauthorized>> = vec![];
if vin.is_empty() && self.vout.is_empty() {
None
} else {
Some(transparent::Bundle {
vin,
vout: self.vout.clone(),
vout: self.vout,
authorization: Unauthorized {
#[cfg(feature = "transparent-inputs")]
secp: self.secp,
#[cfg(feature = "transparent-inputs")]
inputs: self.inputs,
},
})
}
}
}
impl TxIn<Unauthorized> {
#[cfg(feature = "transparent-inputs")]
pub fn create_signatures(
self,
mtx: &TransactionData<Unauthorized>,
txid_parts_cache: &TxDigests<Blake2bHash>,
) -> Option<Vec<Script>> {
if self.inputs.is_empty() && self.vout.is_empty() {
None
} else {
Some(
self.inputs
.iter()
.enumerate()
.map(|(i, info)| {
let sighash = signature_hash(
mtx,
SignableInput::transparent(
i,
&info.coin.script_pubkey,
info.coin.value,
),
txid_parts_cache,
SIGHASH_ALL,
);
let msg =
secp256k1::Message::from_slice(sighash.as_ref()).expect("32 bytes");
let sig = self.secp.sign(&msg, &info.sk);
// Signature has to have "SIGHASH_ALL" appended to it
let mut sig_bytes: Vec<u8> = sig.serialize_der()[..].to_vec();
sig_bytes.extend(&[SIGHASH_ALL as u8]);
// P2PKH scriptSig
Script::default() << &sig_bytes[..] << &info.pubkey[..]
})
.collect(),
)
#[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))]
pub fn new(prevout: OutPoint) -> Self {
TxIn {
prevout,
script_sig: (),
sequence: std::u32::MAX,
}
}
}
impl Bundle<Unauthorized> {
pub fn apply_signatures(
self,
#[cfg(feature = "transparent-inputs")] mtx: &TransactionData<tx::Unauthorized>,
#[cfg(feature = "transparent-inputs")] txid_parts_cache: &TxDigests<Blake2bHash>,
) -> Bundle<Authorized> {
#[cfg(feature = "transparent-inputs")]
let script_sigs: Vec<Script> = self
.authorization
.inputs
.iter()
.enumerate()
.map(|(i, info)| {
let sighash = signature_hash(
mtx,
SignableInput::transparent(i, &info.coin.script_pubkey, info.coin.value),
txid_parts_cache,
SIGHASH_ALL,
);
let msg = secp256k1::Message::from_slice(sighash.as_ref()).expect("32 bytes");
let sig = self.authorization.secp.sign(&msg, &info.sk);
// Signature has to have "SIGHASH_ALL" appended to it
let mut sig_bytes: Vec<u8> = sig.serialize_der()[..].to_vec();
sig_bytes.extend(&[SIGHASH_ALL as u8]);
// P2PKH scriptSig
Script::default() << &sig_bytes[..] << &info.pubkey[..]
})
.collect();
#[cfg(not(feature = "transparent-inputs"))]
let script_sigs = vec![];
transparent::Bundle {
vin: self
.vin
.into_iter()
.zip(script_sigs.into_iter())
.map(|(txin, sig)| TxIn {
prevout: txin.prevout,
script_sig: sig,
sequence: txin.sequence,
})
.collect(),
vout: self.vout,
authorization: Authorized,
}
}
}

View File

@ -256,7 +256,7 @@ impl Authorization for Authorized {
pub struct Unauthorized;
impl Authorization for Unauthorized {
type TransparentAuth = transparent::Unauthorized;
type TransparentAuth = transparent::builder::Unauthorized;
type SaplingAuth = sapling::Unauthorized;
type OrchardAuth = orchard::builder::Unauthorized;
@ -617,7 +617,11 @@ impl Transaction {
Ok(if vin.is_empty() && vout.is_empty() {
None
} else {
Some(transparent::Bundle { vin, vout })
Some(transparent::Bundle {
vin,
vout,
authorization: transparent::Authorized,
})
})
}