Skip to content

Commit 559e4ed

Browse files
committed
Merge #128: Check if psbt is final, before broadcasting
110be3f check if psbt is final, before broadcasting (rajarshimaitra) Pull request description: <!-- You can erase any parts of this template not applicable to your Pull Request. --> ### Description Fixes #97 Add a finalizer check before broadcasting psbts. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk-cli/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing ACKs for top commit: notmandatory: tACK 110be3f Tree-SHA512: 898908062bbfbe90ed296afbd83179066bfc2cb42538b3ff1c91a6da6c174ec4dbb0cd013dd766ec12ea944b7f0673ad36552aa0a423d2afbc94edcf43084d5c
2 parents 6dc50fc + 110be3f commit 559e4ed

File tree

1 file changed

+57
-0
lines changed

1 file changed

+57
-0
lines changed

src/handlers.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ where
331331
(Some(psbt), None) => {
332332
let psbt = base64::decode(&psbt).map_err(|e| Error::Generic(e.to_string()))?;
333333
let psbt: PartiallySignedTransaction = deserialize(&psbt)?;
334+
is_final(&psbt)?;
334335
psbt.extract_tx()
335336
}
336337
(None, Some(tx)) => deserialize(&Vec::<u8>::from_hex(&tx)?)?,
@@ -388,6 +389,37 @@ where
388389
}
389390
}
390391

392+
/// Determine if PSBT has final script sigs or witnesses for all unsigned tx inputs.
393+
#[cfg(any(
394+
feature = "electrum",
395+
feature = "esplora",
396+
feature = "compact_filters",
397+
feature = "rpc"
398+
))]
399+
pub(crate) fn is_final(psbt: &PartiallySignedTransaction) -> Result<(), Error> {
400+
let unsigned_tx_inputs = psbt.unsigned_tx.input.len();
401+
let psbt_inputs = psbt.inputs.len();
402+
if unsigned_tx_inputs != psbt_inputs {
403+
return Err(Error::Generic(format!(
404+
"Malformed PSBT, {} unsigned tx inputs and {} psbt inputs.",
405+
unsigned_tx_inputs, psbt_inputs
406+
)));
407+
}
408+
let sig_count = psbt.inputs.iter().fold(0, |count, input| {
409+
if input.final_script_sig.is_some() || input.final_script_witness.is_some() {
410+
count + 1
411+
} else {
412+
count
413+
}
414+
});
415+
if unsigned_tx_inputs > sig_count {
416+
return Err(Error::Generic(
417+
"The PSBT is not finalized, inputs are are not fully signed.".to_string(),
418+
));
419+
}
420+
Ok(())
421+
}
422+
391423
/// Handle a key sub-command
392424
///
393425
/// Key sub-commands are described in [`KeySubCommand`].
@@ -714,3 +746,28 @@ pub(crate) fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
714746
};
715747
result.map_err(|e| e.into())
716748
}
749+
750+
#[cfg(test)]
751+
mod test {
752+
use super::*;
753+
use bdk::bitcoin::psbt::PartiallySignedTransaction;
754+
use std::str::FromStr;
755+
756+
#[cfg(any(
757+
feature = "electrum",
758+
feature = "esplora",
759+
feature = "compact_filters",
760+
feature = "rpc"
761+
))]
762+
#[test]
763+
fn test_psbt_is_final() {
764+
let unsigned_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
765+
assert!(is_final(&unsigned_psbt).is_err());
766+
767+
let part_signed_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOyICA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDSDBFAiEAnNPpu6wNX2HXYz8s2q5nXug4cWfvCGD3SSH2CNKm+yECIEQO7/URhUPsGoknMTE+GrYJf9Wxqn9QsuN9FGj32cQpAQEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEAACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
768+
assert!(is_final(&part_signed_psbt).is_err());
769+
770+
let full_signed_psbt = PartiallySignedTransaction::from_str("cHNidP8BAIkBAAAAASWJHzxzyVORV/C3lAynKHVVL7+Rw7/Jj8U9fuvD24olAAAAAAD+////AiBOAAAAAAAAIgAgLzY9yE4jzTFJnHtTjkc+rFAtJ9NB7ENFQ1xLYoKsI1cfqgKVAAAAACIAIFsbWgDeLGU8EA+RGwBDIbcv4gaGG0tbEIhDvwXXa/E7LwEAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BALLAAD/////AgD5ApUAAAAAIgAgWxtaAN4sZTwQD5EbAEMhty/iBoYbS1sQiEO/Bddr8TsAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErAPkClQAAAAAiACBbG1oA3ixlPBAPkRsAQyG3L+IGhhtLWxCIQ78F12vxOwEFR1IhA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDIQLKhV/gEZYmlsQXnsL5/Uqv5Y8O31tmWW1LQqIBkiqzCVKuIgYCyoVf4BGWJpbEF57C+f1Kr+WPDt9bZlltS0KiAZIqswkEboH3lCIGA/JV2U/0pXW+iP49QcsYilEvkZEd4phmDM8nV8wC+MeDBDS6ZSEBBwABCNsEAEgwRQIhAJzT6busDV9h12M/LNquZ17oOHFn7whg90kh9gjSpvshAiBEDu/1EYVD7BqJJzExPhq2CX/Vsap/ULLjfRRo99nEKQFHMEQCIGoFCvJ2zPB7PCpznh4+1jsY03kMie49KPoPDdr7/T9TAiB3jV7wzR9BH11FSbi+8U8gSX95PrBlnp1lOBgTUIUw3QFHUiED8lXZT/Sldb6I/j1ByxiKUS+RkR3imGYMzydXzAL4x4MhAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJUq4AACICAsqFX+ARliaWxBeewvn9Sq/ljw7fW2ZZbUtCogGSKrMJBG6B95QiAgPyVdlP9KV1voj+PUHLGIpRL5GRHeKYZgzPJ1fMAvjHgwQ0umUhAA==").unwrap();
771+
assert!(is_final(&full_signed_psbt).is_ok());
772+
}
773+
}

0 commit comments

Comments
 (0)