Create a PSBT signer from an ExtendedDescriptor
This commit is contained in:
parent
fa9a62fbee
commit
2c179dd1b8
@ -9,3 +9,4 @@ bitcoin = { version = "0.23", features = ["use-serde"] }
|
|||||||
miniscript = { version = "0.12" }
|
miniscript = { version = "0.12" }
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
serde_json = { version = "^1.0" }
|
serde_json = { version = "^1.0" }
|
||||||
|
base64 = "^0.11"
|
||||||
|
50
core/lib/examples/psbt.rs
Normal file
50
core/lib/examples/psbt.rs
Normal file
@ -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)));
|
||||||
|
}
|
@ -94,11 +94,7 @@ where
|
|||||||
trait Key: std::fmt::Debug {
|
trait Key: std::fmt::Debug {
|
||||||
fn fingerprint(&self, secp: &Secp256k1<All>) -> Option<Fingerprint>;
|
fn fingerprint(&self, secp: &Secp256k1<All>) -> Option<Fingerprint>;
|
||||||
fn as_public_key(&self, secp: &Secp256k1<All>, index: Option<u32>) -> Result<PublicKey, Error>;
|
fn as_public_key(&self, secp: &Secp256k1<All>, index: Option<u32>) -> Result<PublicKey, Error>;
|
||||||
fn as_secret_key(
|
fn as_secret_key(&self) -> Option<PrivateKey>;
|
||||||
&self,
|
|
||||||
secp: &Secp256k1<All>,
|
|
||||||
index: Option<u32>,
|
|
||||||
) -> Result<Option<PrivateKey>, Error>;
|
|
||||||
fn xprv(&self) -> Option<ExtendedPrivKey>;
|
fn xprv(&self) -> Option<ExtendedPrivKey>;
|
||||||
fn full_path(&self, index: u32) -> Option<DerivationPath>;
|
fn full_path(&self, index: u32) -> Option<DerivationPath>;
|
||||||
fn is_fixed(&self) -> bool;
|
fn is_fixed(&self) -> bool;
|
||||||
@ -117,12 +113,8 @@ impl Key for PublicKey {
|
|||||||
Ok(PublicKey::clone(self))
|
Ok(PublicKey::clone(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_secret_key(
|
fn as_secret_key(&self) -> Option<PrivateKey> {
|
||||||
&self,
|
None
|
||||||
_secp: &Secp256k1<All>,
|
|
||||||
_index: Option<u32>,
|
|
||||||
) -> Result<Option<PrivateKey>, Error> {
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
||||||
@ -151,12 +143,8 @@ impl Key for PrivateKey {
|
|||||||
Ok(self.public_key(secp))
|
Ok(self.public_key(secp))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_secret_key(
|
fn as_secret_key(&self) -> Option<PrivateKey> {
|
||||||
&self,
|
Some(PrivateKey::clone(self))
|
||||||
_secp: &Secp256k1<All>,
|
|
||||||
_index: Option<u32>,
|
|
||||||
) -> Result<Option<PrivateKey>, Error> {
|
|
||||||
Ok(Some(PrivateKey::clone(self)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
||||||
@ -181,22 +169,8 @@ impl Key for DescriptorExtendedKey {
|
|||||||
Ok(self.derive_xpub(secp, index.unwrap_or(0))?.public_key)
|
Ok(self.derive_xpub(secp, index.unwrap_or(0))?.public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_secret_key(
|
fn as_secret_key(&self) -> Option<PrivateKey> {
|
||||||
&self,
|
None
|
||||||
secp: &Secp256k1<All>,
|
|
||||||
index: Option<u32>,
|
|
||||||
) -> Result<Option<PrivateKey>, 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 xprv(&self) -> Option<ExtendedPrivKey> {
|
fn xprv(&self) -> Option<ExtendedPrivKey> {
|
||||||
@ -295,6 +269,14 @@ impl ExtendedDescriptor {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_secret_keys(&self) -> Vec<PrivateKey> {
|
||||||
|
self.keys
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, v)| v.as_secret_key().is_some())
|
||||||
|
.map(|(_, v)| v.as_secret_key().unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_hd_keypaths(
|
pub fn get_hd_keypaths(
|
||||||
&self,
|
&self,
|
||||||
index: u32,
|
index: u32,
|
||||||
|
@ -4,5 +4,8 @@ pub extern crate miniscript;
|
|||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
pub mod descriptor;
|
#[macro_use]
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod descriptor;
|
||||||
|
pub mod psbt;
|
||||||
|
pub mod signer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user