Store partial authorizing data for transparent txs in transparent authorization.
This commit is contained in:
parent
dac68ce2aa
commit
ab1b31ebf6
|
@ -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))
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue