diff --git a/Cargo.lock b/Cargo.lock index b95d26f..90fcf25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -335,7 +335,7 @@ dependencies = [ [[package]] name = "bp-derive" version = "0.11.0-beta.6" -source = "git+https://github.com/BP-WG/bp-std?branch=signer#a40a4ea21fb1db8372c4c0b902d18c68b1a055f0" +source = "git+https://github.com/BP-WG/bp-std?branch=signer#7e0ad15eac0fd7d31692b66fd35cad82bc0da735" dependencies = [ "amplify", "bitcoin_hashes 0.14.0", @@ -383,7 +383,7 @@ dependencies = [ [[package]] name = "bp-invoice" version = "0.11.0-beta.6" -source = "git+https://github.com/BP-WG/bp-std?branch=signer#a40a4ea21fb1db8372c4c0b902d18c68b1a055f0" +source = "git+https://github.com/BP-WG/bp-std?branch=signer#7e0ad15eac0fd7d31692b66fd35cad82bc0da735" dependencies = [ "amplify", "bech32", @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.6" -source = "git+https://github.com/BP-WG/bp-std?branch=signer#a40a4ea21fb1db8372c4c0b902d18c68b1a055f0" +source = "git+https://github.com/BP-WG/bp-std?branch=signer#7e0ad15eac0fd7d31692b66fd35cad82bc0da735" dependencies = [ "amplify", "bp-consensus", @@ -700,7 +700,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.6" -source = "git+https://github.com/BP-WG/bp-std?branch=signer#a40a4ea21fb1db8372c4c0b902d18c68b1a055f0" +source = "git+https://github.com/BP-WG/bp-std?branch=signer#7e0ad15eac0fd7d31692b66fd35cad82bc0da735" dependencies = [ "amplify", "bp-derive", @@ -1215,7 +1215,7 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.6" -source = "git+https://github.com/BP-WG/bp-std?branch=signer#a40a4ea21fb1db8372c4c0b902d18c68b1a055f0" +source = "git+https://github.com/BP-WG/bp-std?branch=signer#7e0ad15eac0fd7d31692b66fd35cad82bc0da735" dependencies = [ "amplify", "base64", diff --git a/src/cli/command.rs b/src/cli/command.rs index 85063e9..8ad38c1 100644 --- a/src/cli/command.rs +++ b/src/cli/command.rs @@ -24,11 +24,12 @@ use std::convert::Infallible; use std::fs::File; use std::path::PathBuf; use std::process::exit; -use std::{error, fs}; +use std::{error, fs, io}; +use amplify::IoError; use bpstd::psbt::{Beneficiary, TxParams}; -use bpstd::{Derive, IdxBase, Keychain, NormalIndex, Sats}; -use psbt::{ConstructionError, Payment, PsbtConstructor, PsbtVer}; +use bpstd::{ConsensusEncode, Derive, IdxBase, Keychain, NormalIndex, Sats}; +use psbt::{ConstructionError, Payment, Psbt, PsbtConstructor, PsbtVer}; use strict_encoding::Ident; use crate::cli::{Args, Config, DescriptorOpts, Exec}; @@ -132,12 +133,30 @@ pub enum BpCommand { /// Name of PSBT file to save. If not given, prints PSBT to STDOUT psbt: Option, }, + + /// Finalizes PSBT, optionally extracting and publishing the signed transaction. + #[display("finalize")] + Finalize { + /// Extract and send the signed transaction to the network. + #[clap(short, long)] + publish: bool, + + /// Name of PSBT file to finalize. + psbt: PathBuf, + + /// File to save the extracted signed transaction. + tx: Option, + }, } #[derive(Debug, Display, Error, From)] #[non_exhaustive] #[display(inner)] pub enum ExecError { + #[from] + #[from(io::Error)] + Io(IoError), + #[from] Load(LoadError), @@ -147,6 +166,9 @@ pub enum ExecError { #[from] ConstructPsbt(ConstructionError), + #[from] + DecodePsbt(psbt::DecodeError), + #[cfg(feature = "electrum")] /// error querying electrum server. /// @@ -431,6 +453,45 @@ impl Exec for Args { }, } } + BpCommand::Finalize { publish, psbt, tx } => { + eprint!("Reading PSBT from file {} ... ", psbt.display()); + let mut psbt_file = File::open(psbt)?; + let mut psbt = Psbt::decode(&mut psbt_file)?; + eprintln!("success"); + if psbt.is_finalized() { + eprintln!("The PSBT is already finalized"); + } else { + let wallet = self.bp_wallet::(&config)?; + eprint!("Finalizing PSBT ... "); + let inputs = psbt.finalize(wallet.descriptor()); + eprint!("{inputs} of {} inputs were finalized", psbt.inputs().count()); + if psbt.is_finalized() { + eprintln!(", transaction is ready for the extraction"); + } else { + eprintln!(", but some non-finalized inputs remains"); + } + } + if *publish || tx.is_some() { + eprint!("Extracting signed transaction ... "); + match psbt.extract() { + Ok(extracted) => { + eprintln!("success"); + if let Some(file) = tx { + eprint!("Saving transaction to file {} ...", file.display()); + let mut file = File::create(file)?; + extracted.consensus_encode(&mut file)?; + eprintln!("success"); + } + if *publish { + todo!("sending transaction to network") + } + } + Err(e) => { + eprintln!("PSBT still contains {} non-finalized inputs, failing", e.0); + } + } + } + } }; println!();