Skip to content

Commit aacd7ee

Browse files
committed
Populate more taproot fields in PSBTs
1 parent 4e46b17 commit aacd7ee

File tree

1 file changed

+69
-7
lines changed

1 file changed

+69
-7
lines changed

src/wallet/mod.rs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ use std::sync::Arc;
2424
use bitcoin::secp256k1::Secp256k1;
2525

2626
use bitcoin::consensus::encode::serialize;
27-
use bitcoin::util::psbt;
27+
use bitcoin::util::{psbt, taproot};
2828
use bitcoin::{
2929
Address, EcdsaSighashType, Network, OutPoint, Script, Transaction, TxOut, Txid, Witness,
3030
};
3131

3232
use miniscript::descriptor::DescriptorTrait;
3333
use miniscript::psbt::PsbtInputSatisfier;
34+
use miniscript::ToPublicKey;
3435

3536
#[allow(unused_imports)]
3637
use log::{debug, error, info, trace};
@@ -1432,8 +1433,10 @@ where
14321433
psbt_input: foreign_psbt_input,
14331434
outpoint,
14341435
} => {
1435-
// TODO: do not require non_witness_utxo for taproot utxos
1436-
if !params.only_witness_utxo && foreign_psbt_input.non_witness_utxo.is_none() {
1436+
if foreign_psbt_input.tap_internal_key.is_none()
1437+
&& !params.only_witness_utxo
1438+
&& foreign_psbt_input.non_witness_utxo.is_none()
1439+
{
14371440
return Err(Error::Generic(format!(
14381441
"Missing non_witness_utxo on foreign utxo {}",
14391442
outpoint
@@ -1458,10 +1461,27 @@ where
14581461
let (desc, _) = self._get_descriptor_for_keychain(keychain);
14591462
let derived_descriptor = desc.as_derived(child, &self.secp);
14601463

1461-
if desc.is_taproot() {
1464+
if let miniscript::Descriptor::Tr(tr) = &derived_descriptor {
1465+
let tap_tree = if tr.taptree().is_some() {
1466+
let mut builder = taproot::TaprootBuilder::new();
1467+
for (depth, ms) in tr.iter_scripts() {
1468+
let script = ms.encode();
1469+
builder = builder.add_leaf(depth, script).expect(
1470+
"Computing spend data on a valid Tree should always succeed",
1471+
);
1472+
}
1473+
Some(
1474+
psbt::TapTree::from_builder(builder)
1475+
.expect("The tree should always be valid"),
1476+
)
1477+
} else {
1478+
None
1479+
};
1480+
psbt_output.tap_tree = tap_tree;
14621481
psbt_output
14631482
.tap_key_origins
14641483
.append(&mut derived_descriptor.get_tap_key_origins(&self.secp));
1484+
psbt_output.tap_internal_key = Some(tr.internal_key().to_x_only_pubkey());
14651485
} else {
14661486
psbt_output
14671487
.bip32_derivation
@@ -1499,8 +1519,22 @@ where
14991519

15001520
let desc = self.get_descriptor_for_keychain(keychain);
15011521
let derived_descriptor = desc.as_derived(child, &self.secp);
1502-
if desc.is_taproot() {
1522+
1523+
if let miniscript::Descriptor::Tr(tr) = &derived_descriptor {
15031524
psbt_input.tap_key_origins = derived_descriptor.get_tap_key_origins(&self.secp);
1525+
psbt_input.tap_internal_key = Some(tr.internal_key().to_x_only_pubkey());
1526+
1527+
let spend_info = tr.spend_info();
1528+
psbt_input.tap_merkle_root = spend_info.merkle_root();
1529+
psbt_input.tap_scripts = spend_info
1530+
.as_script_map()
1531+
.keys()
1532+
.filter_map(|script_ver| {
1533+
spend_info
1534+
.control_block(script_ver)
1535+
.map(|cb| (cb, script_ver.clone()))
1536+
})
1537+
.collect();
15041538
} else {
15051539
psbt_input.bip32_derivation = derived_descriptor.get_hd_keypaths(&self.secp);
15061540
}
@@ -1783,6 +1817,10 @@ pub(crate) mod test {
17831817
"tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*)"
17841818
}
17851819

1820+
pub(crate) fn get_test_tr_with_taptree() -> &'static str {
1821+
"tr(cPZzKuNmpuUjD1e8jUU4PVzy2b5LngbSip8mBsxf4e7rSFZVb4Uh,{pk(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
1822+
}
1823+
17861824
macro_rules! assert_fee_rate {
17871825
($tx:expr, $fees:expr, $fee_rate:expr $( ,@dust_change $( $dust_change:expr )* )* $( ,@add_signature $( $add_signature:expr )* )* ) => ({
17881826
let mut tx = $tx.clone();
@@ -4098,8 +4136,32 @@ pub(crate) mod test {
40984136
builder.add_recipient(addr.script_pubkey(), 25_000);
40994137
let (psbt, _) = builder.finish().unwrap();
41004138

4101-
assert_eq!(psbt.inputs[0].tap_key_origins.len(), 1, "Empty input tap_key_origins");
4102-
assert_eq!(psbt.outputs[0].tap_key_origins.len(), 1, "Empty output tap_key_origins");
4139+
assert_eq!(
4140+
psbt.inputs[0].tap_key_origins.len(),
4141+
1,
4142+
"Empty input tap_key_origins"
4143+
);
4144+
assert_eq!(
4145+
psbt.outputs[0].tap_key_origins.len(),
4146+
1,
4147+
"Empty output tap_key_origins"
4148+
);
4149+
}
4150+
4151+
#[test]
4152+
fn test_taproot_psbt_input_tap_tree() {
4153+
let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree());
4154+
let addr = wallet.get_address(AddressIndex::New).unwrap();
4155+
4156+
let mut builder = wallet.build_tx();
4157+
builder.add_recipient(addr.script_pubkey(), 25_000);
4158+
let (psbt, _) = builder.finish().unwrap();
4159+
4160+
assert!(
4161+
psbt.inputs[0].tap_merkle_root.is_some(),
4162+
"Empty input tap_merkle_root"
4163+
);
4164+
assert_eq!(psbt.inputs[0].tap_scripts.len(), 2, "Empty tap_scripts");
41034165
}
41044166
}
41054167

0 commit comments

Comments
 (0)