descriptor: encapsulate PSBT in/out information update
It'll make it easier to switch to update Taproot info.
This commit is contained in:
parent
f17092375e
commit
85fdc40366
@ -67,10 +67,12 @@ fuzz_target!(|data: &[u8]| {
|
||||
desc.receive_descriptor().derive(der_index, &SECP256K1),
|
||||
desc.change_descriptor().derive(der_index, &SECP256K1),
|
||||
];
|
||||
let mut psbt_in = Default::default();
|
||||
let mut psbt_out = Default::default();
|
||||
for desc in der_descs {
|
||||
desc.address(Network::Bitcoin);
|
||||
desc.script_pubkey();
|
||||
desc.witness_script();
|
||||
desc.bip32_derivations();
|
||||
desc.update_psbt_in(&mut psbt_in);
|
||||
desc.update_change_psbt_out(&mut psbt_out);
|
||||
}
|
||||
});
|
||||
|
||||
@ -154,11 +154,13 @@ fuzz_target!(|config: Config| {
|
||||
desc.receive_descriptor().derive(der_index, &SECP256K1),
|
||||
desc.change_descriptor().derive(der_index, &SECP256K1),
|
||||
];
|
||||
let mut psbt_in = Default::default();
|
||||
let mut psbt_out = Default::default();
|
||||
for desc in &der_descs {
|
||||
desc.address(Network::Bitcoin);
|
||||
desc.script_pubkey();
|
||||
desc.witness_script();
|
||||
desc.bip32_derivations();
|
||||
desc.update_psbt_in(&mut psbt_in);
|
||||
desc.update_change_psbt_out(&mut psbt_out);
|
||||
}
|
||||
|
||||
// Exercise the methods gathering information from a PSBT. TODO: get more useful PSBTs.
|
||||
@ -172,12 +174,11 @@ fuzz_target!(|config: Config| {
|
||||
// for outputs.
|
||||
let rec_desc = &der_descs[0];
|
||||
for psbt_in in psbt.inputs.iter_mut() {
|
||||
psbt_in.witness_script = Some(rec_desc.witness_script());
|
||||
psbt_in.bip32_derivation = rec_desc.bip32_derivations();
|
||||
rec_desc.update_psbt_in(psbt_in);
|
||||
}
|
||||
let change_desc = &der_descs[1];
|
||||
for psbt_out in psbt.outputs.iter_mut() {
|
||||
psbt_out.bip32_derivation = change_desc.bip32_derivations();
|
||||
change_desc.update_change_psbt_out(psbt_out);
|
||||
}
|
||||
|
||||
// Now get the spend info again with these info.
|
||||
|
||||
@ -2,7 +2,7 @@ use miniscript::{
|
||||
bitcoin::{
|
||||
self, bip32,
|
||||
constants::WITNESS_SCALE_FACTOR,
|
||||
psbt::{Input as PsbtIn, Psbt},
|
||||
psbt::{Input as PsbtIn, Output as PsbtOut, Psbt},
|
||||
secp256k1,
|
||||
},
|
||||
descriptor, translate_hash_clone, ForEachKey, TranslatePk, Translator,
|
||||
@ -519,11 +519,11 @@ impl DerivedSinglePathLianaDesc {
|
||||
self.0.script_pubkey()
|
||||
}
|
||||
|
||||
pub fn witness_script(&self) -> bitcoin::ScriptBuf {
|
||||
fn witness_script(&self) -> bitcoin::ScriptBuf {
|
||||
self.0.explicit_script().expect("Not a Taproot descriptor")
|
||||
}
|
||||
|
||||
pub fn bip32_derivations(&self) -> Bip32Deriv {
|
||||
fn bip32_derivations(&self) -> Bip32Deriv {
|
||||
let ms = match self.0 {
|
||||
descriptor::Descriptor::Wsh(ref wsh) => match wsh.as_inner() {
|
||||
descriptor::WshInner::Ms(ms) => ms,
|
||||
@ -539,6 +539,18 @@ impl DerivedSinglePathLianaDesc {
|
||||
.map(|k| (k.key.inner, (k.origin.0, k.origin.1)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Update the PSBT input information with data from this derived descriptor.
|
||||
pub fn update_psbt_in(&self, psbtin: &mut PsbtIn) {
|
||||
psbtin.bip32_derivation = self.bip32_derivations();
|
||||
psbtin.witness_script = Some(self.witness_script());
|
||||
}
|
||||
|
||||
/// Update the info of a PSBT output for a change output with data from this derived
|
||||
/// descriptor.
|
||||
pub fn update_change_psbt_out(&self, psbtout: &mut PsbtOut) {
|
||||
psbtout.bip32_derivation = self.bip32_derivations();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -833,8 +845,10 @@ mod tests {
|
||||
|
||||
// Sanity check we can call the methods on the derived desc
|
||||
der_desc.script_pubkey();
|
||||
der_desc.witness_script();
|
||||
assert!(!der_desc.bip32_derivations().is_empty());
|
||||
let mut psbt_in = PsbtIn::default();
|
||||
der_desc.update_psbt_in(&mut psbt_in);
|
||||
assert!(psbt_in.witness_script.is_some());
|
||||
assert!(!psbt_in.bip32_derivation.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -427,6 +427,12 @@ mod tests {
|
||||
// Create a dummy PSBT spending a coin from this descriptor with a single input and single
|
||||
// (external) output. We'll be modifying it as we go.
|
||||
let spent_coin_desc = desc.receive_descriptor().derive(42.into(), &secp);
|
||||
let mut psbt_in = PsbtIn::default();
|
||||
spent_coin_desc.update_psbt_in(&mut psbt_in);
|
||||
psbt_in.witness_utxo = Some(bitcoin::TxOut {
|
||||
value: Amount::from_sat(19_000),
|
||||
script_pubkey: spent_coin_desc.script_pubkey(),
|
||||
});
|
||||
let mut dummy_psbt = Psbt {
|
||||
unsigned_tx: bitcoin::Transaction {
|
||||
version: bitcoin::transaction::Version::TWO,
|
||||
@ -453,15 +459,7 @@ mod tests {
|
||||
xpub: BTreeMap::new(),
|
||||
proprietary: BTreeMap::new(),
|
||||
unknown: BTreeMap::new(),
|
||||
inputs: vec![PsbtIn {
|
||||
witness_script: Some(spent_coin_desc.witness_script()),
|
||||
bip32_derivation: spent_coin_desc.bip32_derivations(),
|
||||
witness_utxo: Some(bitcoin::TxOut {
|
||||
value: Amount::from_sat(19_000),
|
||||
script_pubkey: spent_coin_desc.script_pubkey(),
|
||||
}),
|
||||
..PsbtIn::default()
|
||||
}],
|
||||
inputs: vec![psbt_in],
|
||||
outputs: Vec::new(),
|
||||
};
|
||||
|
||||
@ -492,15 +490,13 @@ mod tests {
|
||||
// We can add another input to the PSBT. If we don't attach also another transaction input
|
||||
// it will fail.
|
||||
let other_spent_coin_desc = desc.receive_descriptor().derive(84.into(), &secp);
|
||||
dummy_psbt.inputs.push(PsbtIn {
|
||||
witness_script: Some(other_spent_coin_desc.witness_script()),
|
||||
bip32_derivation: other_spent_coin_desc.bip32_derivations(),
|
||||
witness_utxo: Some(bitcoin::TxOut {
|
||||
value: Amount::from_sat(19_000),
|
||||
script_pubkey: other_spent_coin_desc.script_pubkey(),
|
||||
}),
|
||||
..PsbtIn::default()
|
||||
let mut psbt_in = PsbtIn::default();
|
||||
other_spent_coin_desc.update_psbt_in(&mut psbt_in);
|
||||
psbt_in.witness_utxo = Some(bitcoin::TxOut {
|
||||
value: Amount::from_sat(19_000),
|
||||
script_pubkey: other_spent_coin_desc.script_pubkey(),
|
||||
});
|
||||
dummy_psbt.inputs.push(psbt_in);
|
||||
let psbt = dummy_psbt.clone();
|
||||
assert!(prim_signer_a
|
||||
.sign_psbt(psbt, &secp)
|
||||
|
||||
46
src/spend.rs
46
src/spend.rs
@ -649,20 +649,17 @@ pub fn create_spend(
|
||||
});
|
||||
// If it's an address of ours, signal it as change to signing devices by adding the
|
||||
// BIP32 derivation path to the PSBT output.
|
||||
let bip32_derivation = if let Some(AddrInfo { index, is_change }) = address.info {
|
||||
let mut psbt_out = PsbtOut::default();
|
||||
if let Some(AddrInfo { index, is_change }) = address.info {
|
||||
let desc = if is_change {
|
||||
main_descriptor.change_descriptor()
|
||||
} else {
|
||||
main_descriptor.receive_descriptor()
|
||||
};
|
||||
desc.derive(index, secp).bip32_derivations()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
psbt_outs.push(PsbtOut {
|
||||
bip32_derivation,
|
||||
..PsbtOut::default()
|
||||
});
|
||||
desc.derive(index, secp)
|
||||
.update_change_psbt_out(&mut psbt_out)
|
||||
}
|
||||
psbt_outs.push(psbt_out);
|
||||
}
|
||||
assert_eq!(tx.output.is_empty(), is_self_send);
|
||||
|
||||
@ -718,24 +715,21 @@ pub fn create_spend(
|
||||
|
||||
// If the change address is ours, tell the signers by setting the BIP32 derivations in the
|
||||
// PSBT output.
|
||||
let bip32_derivation = if let Some(AddrInfo { index, is_change }) = change_addr.info {
|
||||
let mut psbt_out = PsbtOut::default();
|
||||
if let Some(AddrInfo { index, is_change }) = change_addr.info {
|
||||
let desc = if is_change {
|
||||
main_descriptor.change_descriptor()
|
||||
} else {
|
||||
main_descriptor.receive_descriptor()
|
||||
};
|
||||
desc.derive(index, secp).bip32_derivations()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
desc.derive(index, secp)
|
||||
.update_change_psbt_out(&mut psbt_out);
|
||||
}
|
||||
|
||||
// TODO: shuffle once we have Taproot
|
||||
change_txo.value = change_amount;
|
||||
tx.output.push(change_txo);
|
||||
psbt_outs.push(PsbtOut {
|
||||
bip32_derivation,
|
||||
..PsbtOut::default()
|
||||
});
|
||||
psbt_outs.push(psbt_out);
|
||||
} else if max_change_amount.to_sat() > 0 {
|
||||
warnings.push(CreateSpendWarning::ChangeAddedToFee(
|
||||
max_change_amount.to_sat(),
|
||||
@ -762,21 +756,15 @@ pub fn create_spend(
|
||||
});
|
||||
|
||||
// Populate the PSBT input with the information needed by signers.
|
||||
let mut psbt_in = PsbtIn::default();
|
||||
let coin_desc = derived_desc(secp, main_descriptor, cand);
|
||||
let witness_script = Some(coin_desc.witness_script());
|
||||
let witness_utxo = Some(bitcoin::TxOut {
|
||||
coin_desc.update_psbt_in(&mut psbt_in);
|
||||
psbt_in.witness_utxo = Some(bitcoin::TxOut {
|
||||
value: cand.amount,
|
||||
script_pubkey: coin_desc.script_pubkey(),
|
||||
});
|
||||
let non_witness_utxo = tx_getter.get_tx(&cand.outpoint.txid);
|
||||
let bip32_derivation = coin_desc.bip32_derivations();
|
||||
psbt_ins.push(PsbtIn {
|
||||
witness_script,
|
||||
witness_utxo,
|
||||
bip32_derivation,
|
||||
non_witness_utxo,
|
||||
..PsbtIn::default()
|
||||
});
|
||||
psbt_in.non_witness_utxo = tx_getter.get_tx(&cand.outpoint.txid);
|
||||
psbt_ins.push(psbt_in);
|
||||
}
|
||||
|
||||
// Finally, create the PSBT with all inputs and outputs, sanity check it and return it.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user