From 2c179dd1b8eb3712d05a1d4abd7d78c28a79a7b6 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Tue, 4 Feb 2020 11:05:54 +0100 Subject: [PATCH] Create a PSBT signer from an ExtendedDescriptor --- core/lib/Cargo.toml | 1 + core/lib/examples/psbt.rs | 50 ++++++++++++++++++++++++++++++++++ core/lib/src/descriptor/mod.rs | 48 ++++++++++---------------------- core/lib/src/lib.rs | 5 +++- 4 files changed, 70 insertions(+), 34 deletions(-) create mode 100644 core/lib/examples/psbt.rs diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index d9b119a0..6894d5e9 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -9,3 +9,4 @@ bitcoin = { version = "0.23", features = ["use-serde"] } miniscript = { version = "0.12" } serde = { version = "^1.0", features = ["derive"] } serde_json = { version = "^1.0" } +base64 = "^0.11" diff --git a/core/lib/examples/psbt.rs b/core/lib/examples/psbt.rs new file mode 100644 index 00000000..ed0281fc --- /dev/null +++ b/core/lib/examples/psbt.rs @@ -0,0 +1,50 @@ +extern crate base64; +extern crate magical_bitcoin_wallet; + +use std::str::FromStr; + +use magical_bitcoin_wallet::bitcoin; +use magical_bitcoin_wallet::descriptor::*; +use magical_bitcoin_wallet::psbt::*; +use magical_bitcoin_wallet::signer::Signer; + +use bitcoin::consensus::encode::{deserialize, serialize}; +use bitcoin::util::psbt::PartiallySignedTransaction; +use bitcoin::SigHashType; + +fn main() { + let desc = "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/*)"; + + let extended_desc = ExtendedDescriptor::from_str(desc).unwrap(); + + let psbt_str = "cHNidP8BAFMCAAAAAd9SiQfxXZ+CKjgjRNonWXsnlA84aLvjxtwCmMfRc0ZbAQAAAAD+////ASjS9QUAAAAAF6kUYJR3oB0lS1M0W1RRMMiENSX45IuHAAAAAAABAPUCAAAAA9I7/OqeFeOFdr5VTLnj3UI/CNRw2eWmMPf7qDv6uIF6AAAAABcWABTG+kgr0g44V0sK9/9FN9oG/CxMK/7///+d0ffphPcV6FE9J/3ZPKWu17YxBnWWTJQyRJs3HUo1gwEAAAAA/v///835mYd9DmnjVnUKd2421MDoZmIxvB4XyJluN3SPUV9hAAAAABcWABRfvwFGp+x/yWdXeNgFs9v0duyeS/7///8CFbH+AAAAAAAXqRSEnTOAjJN/X6ZgR9ftKmwisNSZx4cA4fUFAAAAABl2qRTs6pS4x17MSQ4yNs/1GPsfdlv2NIisAAAAACIGApVE9PPtkcqp8Da43yrXGv4nLOotZdyxwJoTWQxuLxIuCAxfmh4JAAAAAAA="; + let psbt_buf = base64::decode(psbt_str).unwrap(); + let mut psbt: PartiallySignedTransaction = deserialize(&psbt_buf).unwrap(); + + let signer = PSBTSigner::from_descriptor(&psbt.global.unsigned_tx, &extended_desc).unwrap(); + + for (index, input) in psbt.inputs.iter_mut().enumerate() { + for (pubkey, (fing, path)) in &input.hd_keypaths { + let sighash = input.sighash_type.unwrap_or(SigHashType::All); + + // Ignore the "witness_utxo" case because we know this psbt is a legacy tx + if let Some(non_wit_utxo) = &input.non_witness_utxo { + let prev_script = &non_wit_utxo.output + [psbt.global.unsigned_tx.input[index].previous_output.vout as usize] + .script_pubkey; + let (signature, sighash) = signer + .sig_legacy_from_fingerprint(index, sighash, fing, path, prev_script) + .unwrap() + .unwrap(); + + let mut concat_sig = Vec::new(); + concat_sig.extend_from_slice(&signature.serialize_der()); + concat_sig.extend_from_slice(&[sighash as u8]); + + input.partial_sigs.insert(*pubkey, concat_sig); + } + } + } + + println!("signed: {}", base64::encode(&serialize(&psbt))); +} diff --git a/core/lib/src/descriptor/mod.rs b/core/lib/src/descriptor/mod.rs index d3cb14fe..01c6a0a8 100644 --- a/core/lib/src/descriptor/mod.rs +++ b/core/lib/src/descriptor/mod.rs @@ -94,11 +94,7 @@ where trait Key: std::fmt::Debug { fn fingerprint(&self, secp: &Secp256k1) -> Option; fn as_public_key(&self, secp: &Secp256k1, index: Option) -> Result; - fn as_secret_key( - &self, - secp: &Secp256k1, - index: Option, - ) -> Result, Error>; + fn as_secret_key(&self) -> Option; fn xprv(&self) -> Option; fn full_path(&self, index: u32) -> Option; fn is_fixed(&self) -> bool; @@ -117,12 +113,8 @@ impl Key for PublicKey { Ok(PublicKey::clone(self)) } - fn as_secret_key( - &self, - _secp: &Secp256k1, - _index: Option, - ) -> Result, Error> { - Ok(None) + fn as_secret_key(&self) -> Option { + None } fn xprv(&self) -> Option { @@ -151,12 +143,8 @@ impl Key for PrivateKey { Ok(self.public_key(secp)) } - fn as_secret_key( - &self, - _secp: &Secp256k1, - _index: Option, - ) -> Result, Error> { - Ok(Some(PrivateKey::clone(self))) + fn as_secret_key(&self) -> Option { + Some(PrivateKey::clone(self)) } fn xprv(&self) -> Option { @@ -181,22 +169,8 @@ impl Key for DescriptorExtendedKey { Ok(self.derive_xpub(secp, index.unwrap_or(0))?.public_key) } - fn as_secret_key( - &self, - secp: &Secp256k1, - index: Option, - ) -> Result, Error> { - if self.secret.is_none() { - return Ok(None); - } - - let derivation_path = self.full_path(index.unwrap_or(0)); - Ok(Some( - self.secret - .unwrap() - .derive_priv(secp, &derivation_path)? - .private_key, - )) + fn as_secret_key(&self) -> Option { + None } fn xprv(&self) -> Option { @@ -295,6 +269,14 @@ impl ExtendedDescriptor { .collect() } + pub fn get_secret_keys(&self) -> Vec { + self.keys + .iter() + .filter(|(_, v)| v.as_secret_key().is_some()) + .map(|(_, v)| v.as_secret_key().unwrap()) + .collect() + } + pub fn get_hd_keypaths( &self, index: u32, diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 16d34ebe..242393c9 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -4,5 +4,8 @@ pub extern crate miniscript; extern crate serde; extern crate serde_json; -pub mod descriptor; +#[macro_use] pub mod error; +pub mod descriptor; +pub mod psbt; +pub mod signer;