Skip to content

Commit 127cfca

Browse files
committed
test: improve integration test for decode_psbt across various versions
Test improvements for decode_psbt covering version-specific behaviour across Bitcoin Core v17-v30: - Add doc comments explaining version-specific PSBT behavior - Document fee calculation: v17 (no utxoupdatepsbt), v18-v19 (p2sh-segwit default breaks utxoupdatepsbt detection), v20+ (bech32 default works) - Gate xpubs field assertions correctly (v22_and_below vs v23+) - Add taproot field test with tap_internal_key (v24+) - Add TODO for MuSig2 fields (v30) The fee field is None on v17-v19 because: - v17: No utxoupdatepsbt RPC exists - v18-v19: utxoupdatepsbt can't detect p2sh-segwit (default address type) as segwit without descriptors, so UTXO data isn't populated (hence no fee calc) - v20+: Default changed to bech32 (PR #16884), native segwit is directly detected, so fee calc works
1 parent acef71f commit 127cfca

File tree

1 file changed

+49
-10
lines changed

1 file changed

+49
-10
lines changed

integration_test/tests/raw_transactions.rs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,18 +127,37 @@ fn raw_transactions__create_raw_transaction__modelled() {
127127
create_sign_send(&node);
128128
}
129129

130-
// Notes on testing decoding of PBST.
130+
// Notes on testing decoding of PSBT.
131131
//
132-
// - `bip32_derivs` field in the input list of the decoded PSBT changes shape a bunch of times.
133-
// - In v23 a bunch of additional fields are added.
134-
// - In v24 taproot fields are added.
132+
// Version-specific behavior:
133+
// - v17: Base PSBT support via `createpsbt`/`decodepsbt`. No `utxoupdatepsbt`.
134+
// - v18-v19: `utxoupdatepsbt` added but default address type is p2sh-segwit; `utxoupdatepsbt`
135+
// can't detect p2sh-segwit as segwit without descriptors, so UTXO data isn't populated.
136+
// - v20+: Default address type changed to bech32 (PR #16884). Native segwit is directly
137+
// detected, so UTXO data is populated and fee calculation works.
138+
// - v23: Global xpubs field added (previously ended up in `unknown`).
139+
// - v24: Taproot fields added (`tap_internal_key`, etc).
140+
// - v30: MuSig2 fields added. TODO: Test MuSig2.
135141
//
136-
// All this should still be handled by `into_model` because `bitcoin::Psbt` has all optional fields.
142+
// The `bip32_derivs` field in inputs changes shape across versions.
143+
// All version differences should be handled by `into_model` because `bitcoin::Psbt` has optional fields.
137144
#[test]
138145
fn raw_transactions__decode_psbt__modelled() {
139146
let node = Node::with_wallet(Wallet::Default, &["-txindex"]);
140147
node.fund_wallet();
141148

149+
// utxoupdatepsbt (v18+)
150+
// Looks up UTXOs from chain and populates `witness_utxo` field
151+
// so that fee can be calculated (fee = sum(inputs) - sum(outputs)).
152+
#[cfg(not(feature = "v17"))]
153+
let mut psbt = {
154+
let psbt = create_a_psbt(&node);
155+
let updated: UtxoUpdatePsbt = node.client.utxo_update_psbt(&psbt).expect("utxoupdatepsbt");
156+
updated.into_model().expect("UtxoUpdatePsbt into model").0
157+
};
158+
159+
// v17: No utxoupdatepsbt available, so input values are unknown.
160+
#[cfg(feature = "v17")]
142161
let mut psbt = create_a_psbt(&node);
143162

144163
// A bunch of new fields got added in v23.
@@ -158,26 +177,46 @@ fn raw_transactions__decode_psbt__modelled() {
158177
let path =
159178
"m/84'/0'/0'/0/1".parse::<DerivationPath>().expect("failed to parse derivation path");
160179
map.insert(xpub, (fp, path));
161-
162180
psbt.xpub = map;
163181
}
164182

165-
let encoded = psbt.to_string();
183+
// Add an arbitrary tap_internal_key for v24+.
184+
#[cfg(not(feature = "v23_and_below"))]
185+
let tap_key = {
186+
use bitcoin::secp256k1::XOnlyPublicKey;
166187

188+
let tap_key_bytes = <[u8; 32]>::from_hex(
189+
"2afc20bf379bc96a2f4e9e63ffceb8652b2b6a097f63fbee6ecec2a49a48010e",
190+
)
191+
.unwrap();
192+
let key = XOnlyPublicKey::from_slice(&tap_key_bytes).unwrap();
193+
psbt.inputs[0].tap_internal_key = Some(key);
194+
key
195+
};
196+
197+
let encoded = psbt.to_string();
167198
let json: DecodePsbt = node.client.decode_psbt(&encoded).expect("decodepsbt");
168199
let model: Result<mtype::DecodePsbt, DecodePsbtError> = json.into_model();
169-
170-
#[allow(unused_variables)]
171200
let decoded = model.unwrap();
172201

202+
// Fee requires UTXO data. v18/v19 default to p2sh-segwit addresses which utxoupdatepsbt
203+
// can't detect without descriptors. v20+ defaults to bech32 (PR #16884) which works.
204+
#[cfg(feature = "v19_and_below")]
205+
assert!(decoded.fee.is_none());
206+
207+
#[cfg(not(feature = "v19_and_below"))]
208+
assert!(decoded.fee.expect("fee should be present").to_sat() > 0);
209+
173210
// Before Core v23 global xpubs was not a known keypair.
174211
#[cfg(feature = "v22_and_below")]
175212
assert_eq!(decoded.psbt.unknown.len(), 1);
176213

177214
#[cfg(not(feature = "v22_and_below"))]
178215
assert_eq!(decoded.psbt.xpub.len(), 1);
179216

180-
// TODO: Add a taproot field and test it with v24
217+
// v24+ Taproot fields (e.g. `tap_internal_key`).
218+
#[cfg(not(feature = "v23_and_below"))]
219+
assert_eq!(decoded.psbt.inputs[0].tap_internal_key, Some(tap_key));
181220
}
182221

183222
#[test]

0 commit comments

Comments
 (0)