From 57774311355b35c486ec0badfc38ac24c391ad98 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Wed, 12 Aug 2020 12:51:50 +0200 Subject: [PATCH] Use `miniscript::DescriptorPublicKey` This allows us to remove all our custom "ExtendedDescriptor" implementation since that is now built directly in miniscript. --- Cargo.toml | 6 +- examples/parse_descriptor.rs | 10 +- src/blockchain/compact_filters/mod.rs | 4 +- src/blockchain/compact_filters/store.rs | 25 +- src/descriptor/extended_key.rs | 372 ------------ src/descriptor/keys.rs | 181 ------ src/descriptor/mod.rs | 751 +++++++++--------------- src/descriptor/policy.rs | 190 +++--- src/error.rs | 13 +- src/lib.rs | 1 - src/psbt/mod.rs | 279 +-------- src/psbt/utils.rs | 28 - src/signer.rs | 87 --- src/wallet/export.rs | 18 +- src/wallet/mod.rs | 316 ++++------ src/wallet/signer.rs | 327 +++++++++++ src/wallet/utils.rs | 57 ++ testutils-macros/src/lib.rs | 12 +- testutils/src/lib.rs | 21 +- 19 files changed, 935 insertions(+), 1763 deletions(-) delete mode 100644 src/descriptor/extended_key.rs delete mode 100644 src/descriptor/keys.rs delete mode 100644 src/psbt/utils.rs delete mode 100644 src/signer.rs create mode 100644 src/wallet/signer.rs diff --git a/Cargo.toml b/Cargo.toml index 8b5bea3e..f9d4b301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "magical-bitcoin-wallet" version = "0.1.0" edition = "2018" -authors = ["Riccardo Casatta ", "Alekos Filini "] +authors = ["Alekos Filini ", "Riccardo Casatta "] [dependencies] magical-macros = { path = "./macros" } @@ -25,6 +25,10 @@ rocksdb = { version = "0.14", optional = true } socks = { version = "0.3", optional = true } lazy_static = { version = "1.4", optional = true } +[patch.crates-io] +bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin/", rev = "478e091" } +miniscript = { git = "https://github.com/MagicalBitcoin/rust-miniscript", branch = "descriptor-public-key" } + # Platform-specific dependencies [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { version = "0.2", features = ["rt-core"] } diff --git a/examples/parse_descriptor.rs b/examples/parse_descriptor.rs index e21ddb4c..4885973f 100644 --- a/examples/parse_descriptor.rs +++ b/examples/parse_descriptor.rs @@ -1,8 +1,9 @@ extern crate magical_bitcoin_wallet; extern crate serde_json; -use std::str::FromStr; +use std::sync::Arc; +use magical_bitcoin_wallet::bitcoin::util::bip32::ChildNumber; use magical_bitcoin_wallet::bitcoin::*; use magical_bitcoin_wallet::descriptor::*; @@ -14,13 +15,14 @@ fn main() { and_v(vc:pk_h(cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy),older(1000))\ ))"; - let extended_desc = ExtendedDescriptor::from_str(desc).unwrap(); + let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc).unwrap(); println!("{:?}", extended_desc); - let policy = extended_desc.extract_policy().unwrap(); + let signers = Arc::new(key_map.into()); + let policy = extended_desc.extract_policy(signers).unwrap(); println!("policy: {}", serde_json::to_string(&policy).unwrap()); - let derived_desc = extended_desc.derive(42).unwrap(); + let derived_desc = extended_desc.derive(&[ChildNumber::from_normal_idx(42).unwrap()]); println!("{:?}", derived_desc); let addr = derived_desc.address(Network::Testnet).unwrap(); diff --git a/src/blockchain/compact_filters/mod.rs b/src/blockchain/compact_filters/mod.rs index 4e33af46..8e8b0135 100644 --- a/src/blockchain/compact_filters/mod.rs +++ b/src/blockchain/compact_filters/mod.rs @@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex}; use log::{debug, error, info, trace}; use bitcoin::network::message_blockdata::Inventory; -use bitcoin::{BitcoinHash, OutPoint, Transaction, Txid}; +use bitcoin::{OutPoint, Transaction, Txid}; use rocksdb::{Options, SliceTransform, DB}; @@ -274,7 +274,7 @@ impl OnlineBlockchain for CompactFiltersBlockchain { let block_height = headers.get_height_for(block_hash)?.unwrap_or(0); let saved_correct_block = match headers.get_full_block(block_height)? { - Some(block) if &block.bitcoin_hash() == block_hash => true, + Some(block) if &block.block_hash() == block_hash => true, _ => false, }; diff --git a/src/blockchain/compact_filters/store.rs b/src/blockchain/compact_filters/store.rs index bfd570cd..8ea657a8 100644 --- a/src/blockchain/compact_filters/store.rs +++ b/src/blockchain/compact_filters/store.rs @@ -16,7 +16,6 @@ use bitcoin::hash_types::FilterHash; use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::{sha256d, Hash}; use bitcoin::util::bip158::BlockFilter; -use bitcoin::util::hash::BitcoinHash; use bitcoin::util::uint::Uint256; use bitcoin::Block; use bitcoin::BlockHash; @@ -257,7 +256,7 @@ impl ChainStore { ); batch.put_cf( cf_handle, - StoreEntry::BlockHeaderIndex(Some(genesis.bitcoin_hash())).get_key(), + StoreEntry::BlockHeaderIndex(Some(genesis.block_hash())).get_key(), &0usize.to_be_bytes(), ); store.write(batch)?; @@ -290,7 +289,7 @@ impl ChainStore { .get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(index)).get_key())? .unwrap(), )?; - answer.push((header.bitcoin_hash(), index)); + answer.push((header.block_hash(), index)); if let Some(new_index) = index.checked_sub(step) { index = new_index; @@ -322,7 +321,7 @@ impl ChainStore { let mut batch = WriteBatch::default(); batch.put_cf( new_cf_handle, - StoreEntry::BlockHeaderIndex(Some(header.bitcoin_hash())).get_key(), + StoreEntry::BlockHeaderIndex(Some(header.block_hash())).get_key(), &from.to_be_bytes(), ); batch.put_cf( @@ -406,7 +405,7 @@ impl ChainStore { batch.delete_cf( cf_handle, - StoreEntry::BlockHeaderIndex(Some(header.bitcoin_hash())).get_key(), + StoreEntry::BlockHeaderIndex(Some(header.block_hash())).get_key(), ); } @@ -461,7 +460,7 @@ impl ChainStore { .map(|data| { let (header, _): (BlockHeader, Uint256) = deserialize(&data).map_err(|_| CompactFiltersError::DataCorruption)?; - Ok::<_, CompactFiltersError>(header.bitcoin_hash()) + Ok::<_, CompactFiltersError>(header.block_hash()) }) .transpose()?) } @@ -574,7 +573,7 @@ impl ChainStore { .map(|(_, v)| -> Result<_, CompactFiltersError> { let (header, _): (BlockHeader, Uint256) = SerializeDb::deserialize(&v)?; - Ok(header.bitcoin_hash()) + Ok(header.block_hash()) }) .transpose()?) } @@ -593,7 +592,7 @@ impl ChainStore { .get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(from)).get_key())? .map(|result| { let (header, work): (BlockHeader, Uint256) = SerializeDb::deserialize(&result)?; - Ok::<_, CompactFiltersError>((header.bitcoin_hash(), work)) + Ok::<_, CompactFiltersError>((header.block_hash(), work)) }) .transpose()? .ok_or(CompactFiltersError::DataCorruption)?; @@ -603,13 +602,13 @@ impl ChainStore { return Err(CompactFiltersError::InvalidHeaders); } - last_hash = header.bitcoin_hash(); + last_hash = header.block_hash(); accumulated_work = accumulated_work + header.work(); let height = from + index + 1; batch.put_cf( cf_handle, - StoreEntry::BlockHeaderIndex(Some(header.bitcoin_hash())).get_key(), + StoreEntry::BlockHeaderIndex(Some(header.block_hash())).get_key(), &(height).to_be_bytes(), ); batch.put_cf( @@ -647,8 +646,8 @@ pub struct FilterHeader { filter_hash: FilterHash, } -impl BitcoinHash for FilterHeader { - fn bitcoin_hash(&self) -> FilterHeaderHash { +impl FilterHeader { + fn header_hash(&self) -> FilterHeaderHash { let mut hash_data = self.filter_hash.into_inner().to_vec(); hash_data.extend_from_slice(&self.prev_header_hash); sha256d::Hash::hash(&hash_data).into() @@ -794,7 +793,7 @@ impl CFStore { prev_header_hash: last_hash, filter_hash, }; - last_hash = filter_header.bitcoin_hash(); + last_hash = filter_header.header_hash(); filter_header }) diff --git a/src/descriptor/extended_key.rs b/src/descriptor/extended_key.rs deleted file mode 100644 index 70cc208e..00000000 --- a/src/descriptor/extended_key.rs +++ /dev/null @@ -1,372 +0,0 @@ -use std::fmt::{self, Display}; -use std::str::FromStr; - -use bitcoin::hashes::hex::{FromHex, ToHex}; -use bitcoin::secp256k1; -use bitcoin::util::base58; -use bitcoin::util::bip32::{ - ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, -}; -use bitcoin::PublicKey; - -#[allow(unused_imports)] -use log::{debug, error, info, trace}; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum DerivationIndex { - Fixed, - Normal, - Hardened, -} - -impl DerivationIndex { - fn as_path(&self, index: u32) -> DerivationPath { - match self { - DerivationIndex::Fixed => vec![], - DerivationIndex::Normal => vec![ChildNumber::Normal { index }], - DerivationIndex::Hardened => vec![ChildNumber::Hardened { index }], - } - .into() - } -} - -impl Display for DerivationIndex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let chars = match *self { - Self::Fixed => "", - Self::Normal => "/*", - Self::Hardened => "/*'", - }; - - write!(f, "{}", chars) - } -} - -#[derive(Clone, Debug)] -pub struct DescriptorExtendedKey { - pub master_fingerprint: Option, - pub master_derivation: Option, - pub pubkey: ExtendedPubKey, - pub secret: Option, - pub path: DerivationPath, - pub final_index: DerivationIndex, -} - -impl DescriptorExtendedKey { - pub fn full_path(&self, index: u32) -> DerivationPath { - let mut final_path: Vec = Vec::new(); - if let Some(path) = &self.master_derivation { - let path_as_vec: Vec = path.clone().into(); - final_path.extend_from_slice(&path_as_vec); - } - let our_path: Vec = self.path_with_index(index).into(); - final_path.extend_from_slice(&our_path); - - final_path.into() - } - - pub fn path_with_index(&self, index: u32) -> DerivationPath { - let mut final_path: Vec = Vec::new(); - let our_path: Vec = self.path.clone().into(); - final_path.extend_from_slice(&our_path); - let other_path: Vec = self.final_index.as_path(index).into(); - final_path.extend_from_slice(&other_path); - - final_path.into() - } - - pub fn derive( - &self, - ctx: &secp256k1::Secp256k1, - index: u32, - ) -> Result { - Ok(self.derive_xpub(ctx, index)?.public_key) - } - - pub fn derive_xpub( - &self, - ctx: &secp256k1::Secp256k1, - index: u32, - ) -> Result { - if let Some(xprv) = self.secret { - let derive_priv = xprv.derive_priv(ctx, &self.path_with_index(index))?; - Ok(ExtendedPubKey::from_private(ctx, &derive_priv)) - } else { - Ok(self.pubkey.derive_pub(ctx, &self.path_with_index(index))?) - } - } - - pub fn root_xpub( - &self, - ctx: &secp256k1::Secp256k1, - ) -> ExtendedPubKey { - if let Some(ref xprv) = self.secret { - ExtendedPubKey::from_private(ctx, xprv) - } else { - self.pubkey - } - } -} - -impl Display for DescriptorExtendedKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(ref fingerprint) = self.master_fingerprint { - write!(f, "[{}", fingerprint.to_hex())?; - if let Some(ref path) = self.master_derivation { - write!(f, "{}", &path.to_string()[1..])?; - } - write!(f, "]")?; - } - - if let Some(xprv) = self.secret { - write!(f, "{}", xprv)? - } else { - write!(f, "{}", self.pubkey)? - } - - write!(f, "{}{}", &self.path.to_string()[1..], self.final_index) - } -} - -impl FromStr for DescriptorExtendedKey { - type Err = super::Error; - - fn from_str(inp: &str) -> Result { - let len = inp.len(); - - let (master_fingerprint, master_derivation, offset) = match inp.starts_with("[") { - false => (None, None, 0), - true => { - if inp.len() < 9 { - return Err(super::Error::MalformedInput); - } - - let master_fingerprint = &inp[1..9]; - let close_bracket_index = - &inp[9..].find("]").ok_or(super::Error::MalformedInput)?; - let path = if *close_bracket_index > 0 { - Some(DerivationPath::from_str(&format!( - "m{}", - &inp[9..9 + *close_bracket_index] - ))?) - } else { - None - }; - - ( - Some(Fingerprint::from_hex(master_fingerprint)?), - path, - 9 + *close_bracket_index + 1, - ) - } - }; - - let (key_range, offset) = match &inp[offset..].find("/") { - Some(index) => (offset..offset + *index, offset + *index), - None => (offset..len, len), - }; - let data = base58::from_check(&inp[key_range.clone()])?; - let secp = secp256k1::Secp256k1::new(); - let (pubkey, secret) = match &data[0..4] { - [0x04u8, 0x88, 0xB2, 0x1E] | [0x04u8, 0x35, 0x87, 0xCF] => { - (ExtendedPubKey::from_str(&inp[key_range])?, None) - } - [0x04u8, 0x88, 0xAD, 0xE4] | [0x04u8, 0x35, 0x83, 0x94] => { - let private = ExtendedPrivKey::from_str(&inp[key_range])?; - (ExtendedPubKey::from_private(&secp, &private), Some(private)) - } - data => return Err(super::Error::InvalidPrefix(data.into())), - }; - - let (path, final_index, _) = match &inp[offset..].starts_with("/") { - false => (DerivationPath::from(vec![]), DerivationIndex::Fixed, offset), - true => { - let (all, skip) = match &inp[len - 2..len] { - "/*" => (DerivationIndex::Normal, 2), - "*'" | "*h" => (DerivationIndex::Hardened, 3), - _ => (DerivationIndex::Fixed, 0), - }; - - if all == DerivationIndex::Hardened && secret.is_none() { - return Err(super::Error::HardenedDerivationOnXpub); - } - - ( - DerivationPath::from_str(&format!("m{}", &inp[offset..len - skip]))?, - all, - len, - ) - } - }; - - if secret.is_none() - && path.into_iter().any(|child| match child { - ChildNumber::Hardened { .. } => true, - _ => false, - }) - { - return Err(super::Error::HardenedDerivationOnXpub); - } - - Ok(DescriptorExtendedKey { - master_fingerprint, - master_derivation, - pubkey, - secret, - path, - final_index, - }) - } -} - -#[cfg(test)] -mod test { - use std::str::FromStr; - - use bitcoin::hashes::hex::FromHex; - use bitcoin::util::bip32::{ChildNumber, DerivationPath}; - - use crate::descriptor::*; - - macro_rules! hex_fingerprint { - ($hex:expr) => { - Fingerprint::from_hex($hex).unwrap() - }; - } - - macro_rules! deriv_path { - ($str:expr) => { - DerivationPath::from_str($str).unwrap() - }; - - () => { - DerivationPath::from(vec![]) - }; - } - - #[test] - fn test_derivation_index_fixed() { - let index = DerivationIndex::Fixed; - assert_eq!(index.as_path(1337), DerivationPath::from(vec![])); - assert_eq!(format!("{}", index), ""); - } - - #[test] - fn test_derivation_index_normal() { - let index = DerivationIndex::Normal; - assert_eq!( - index.as_path(1337), - DerivationPath::from(vec![ChildNumber::Normal { index: 1337 }]) - ); - assert_eq!(format!("{}", index), "/*"); - } - - #[test] - fn test_derivation_index_hardened() { - let index = DerivationIndex::Hardened; - assert_eq!( - index.as_path(1337), - DerivationPath::from(vec![ChildNumber::Hardened { index: 1337 }]) - ); - assert_eq!(format!("{}", index), "/*'"); - } - - #[test] - fn test_parse_xpub_no_path_fixed() { - let key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.pubkey.fingerprint(), hex_fingerprint!("31a507b8")); - assert_eq!(ek.path, deriv_path!()); - assert_eq!(ek.final_index, DerivationIndex::Fixed); - } - - #[test] - fn test_parse_xpub_with_path_fixed() { - let key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/2/3"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.pubkey.fingerprint(), hex_fingerprint!("31a507b8")); - assert_eq!(ek.path, deriv_path!("m/1/2/3")); - assert_eq!(ek.final_index, DerivationIndex::Fixed); - } - - #[test] - fn test_parse_xpub_with_path_normal() { - let key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/2/3/*"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.pubkey.fingerprint(), hex_fingerprint!("31a507b8")); - assert_eq!(ek.path, deriv_path!("m/1/2/3")); - assert_eq!(ek.final_index, DerivationIndex::Normal); - } - - #[test] - #[should_panic(expected = "HardenedDerivationOnXpub")] - fn test_parse_xpub_with_path_hardened() { - let key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/*'"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.pubkey.fingerprint(), hex_fingerprint!("31a507b8")); - assert_eq!(ek.path, deriv_path!("m/1/2/3")); - assert_eq!(ek.final_index, DerivationIndex::Fixed); - } - - #[test] - fn test_parse_tprv_with_path_hardened() { - let key = "tprv8ZgxMBicQKsPduL5QnGihpprdHyypMGi4DhimjtzYemu7se5YQNcZfAPLqXRuGHb5ZX2eTQj62oNqMnyxJ7B7wz54Uzswqw8fFqMVdcmVF7/1/2/3/*'"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert!(ek.secret.is_some()); - assert_eq!(ek.pubkey.fingerprint(), hex_fingerprint!("5ea4190e")); - assert_eq!(ek.path, deriv_path!("m/1/2/3")); - assert_eq!(ek.final_index, DerivationIndex::Hardened); - } - - #[test] - fn test_parse_xpub_master_details() { - let key = "[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.master_fingerprint, Some(hex_fingerprint!("d34db33f"))); - assert_eq!(ek.master_derivation, Some(deriv_path!("m/44'/0'/0'"))); - } - - #[test] - fn test_parse_xpub_master_details_empty_derivation() { - let key = "[d34db33f]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.master_fingerprint, Some(hex_fingerprint!("d34db33f"))); - assert_eq!(ek.master_derivation, None); - } - - #[test] - #[should_panic(expected = "MalformedInput")] - fn test_parse_xpub_short_input() { - let key = "[d34d"; - DescriptorExtendedKey::from_str(key).unwrap(); - } - - #[test] - #[should_panic(expected = "MalformedInput")] - fn test_parse_xpub_missing_closing_bracket() { - let key = "[d34db33fxpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; - DescriptorExtendedKey::from_str(key).unwrap(); - } - - #[test] - #[should_panic(expected = "InvalidChar")] - fn test_parse_xpub_invalid_fingerprint() { - let key = "[d34db33z]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; - DescriptorExtendedKey::from_str(key).unwrap(); - } - - #[test] - fn test_xpub_normal_full_path() { - let key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/2/*"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.full_path(42), deriv_path!("m/1/2/42")); - } - - #[test] - fn test_xpub_fixed_full_path() { - let key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/2"; - let ek = DescriptorExtendedKey::from_str(key).unwrap(); - assert_eq!(ek.full_path(42), deriv_path!("m/1/2")); - assert_eq!(ek.full_path(1337), deriv_path!("m/1/2")); - } -} diff --git a/src/descriptor/keys.rs b/src/descriptor/keys.rs deleted file mode 100644 index 7ff028d9..00000000 --- a/src/descriptor/keys.rs +++ /dev/null @@ -1,181 +0,0 @@ -use bitcoin::secp256k1::{All, Secp256k1}; -use bitcoin::{PrivateKey, PublicKey}; - -use bitcoin::util::bip32::{ - ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, -}; - -use super::error::Error; -use super::extended_key::DerivationIndex; -use super::DescriptorExtendedKey; - -pub(super) trait Key: std::fmt::Debug + std::fmt::Display { - fn fingerprint(&self, secp: &Secp256k1) -> Option; - fn as_public_key(&self, secp: &Secp256k1, index: Option) -> Result; - fn as_secret_key(&self) -> Option; - fn xprv(&self) -> Option; - fn full_path(&self, index: u32) -> Option; - fn is_fixed(&self) -> bool; - - fn has_secret(&self) -> bool { - self.xprv().is_some() || self.as_secret_key().is_some() - } - - fn public(&self, secp: &Secp256k1) -> Result, Error> { - Ok(Box::new(self.as_public_key(secp, None)?)) - } -} - -impl Key for PublicKey { - fn fingerprint(&self, _secp: &Secp256k1) -> Option { - None - } - - fn as_public_key( - &self, - _secp: &Secp256k1, - _index: Option, - ) -> Result { - Ok(PublicKey::clone(self)) - } - - fn as_secret_key(&self) -> Option { - None - } - - fn xprv(&self) -> Option { - None - } - - fn full_path(&self, _index: u32) -> Option { - None - } - - fn is_fixed(&self) -> bool { - true - } -} - -impl Key for PrivateKey { - fn fingerprint(&self, _secp: &Secp256k1) -> Option { - None - } - - fn as_public_key( - &self, - secp: &Secp256k1, - _index: Option, - ) -> Result { - Ok(self.public_key(secp)) - } - - fn as_secret_key(&self) -> Option { - Some(PrivateKey::clone(self)) - } - - fn xprv(&self) -> Option { - None - } - - fn full_path(&self, _index: u32) -> Option { - None - } - - fn is_fixed(&self) -> bool { - true - } -} - -impl Key for DescriptorExtendedKey { - fn fingerprint(&self, secp: &Secp256k1) -> Option { - if let Some(fing) = self.master_fingerprint { - Some(fing.clone()) - } else { - Some(self.root_xpub(secp).fingerprint()) - } - } - - fn as_public_key(&self, secp: &Secp256k1, index: Option) -> Result { - Ok(self.derive_xpub(secp, index.unwrap_or(0))?.public_key) - } - - fn public(&self, secp: &Secp256k1) -> Result, Error> { - if self.final_index == DerivationIndex::Hardened { - return Err(Error::HardenedDerivationOnXpub); - } - - if self.xprv().is_none() { - return Ok(Box::new(self.clone())); - } - - // copy the part of the path that can be derived on the xpub - let path = self - .path - .into_iter() - .rev() - .take_while(|child| match child { - ChildNumber::Normal { .. } => true, - _ => false, - }) - .cloned() - .collect::>(); - // take the prefix that has to be derived on the xprv - let master_derivation_add = self - .path - .into_iter() - .take(self.path.as_ref().len() - path.len()) - .cloned() - .collect::>(); - let has_derived = !master_derivation_add.is_empty(); - - let derived_xprv = self - .secret - .as_ref() - .unwrap() - .derive_priv(secp, &master_derivation_add)?; - let pubkey = ExtendedPubKey::from_private(secp, &derived_xprv); - - let master_derivation = self - .master_derivation - .as_ref() - .map_or(vec![], |path| path.as_ref().to_vec()) - .into_iter() - .chain(master_derivation_add.into_iter()) - .collect::>(); - let master_derivation = match &master_derivation[..] { - &[] => None, - child_vec => Some(child_vec.into()), - }; - - let master_fingerprint = match self.master_fingerprint { - Some(desc) => Some(desc.clone()), - None if has_derived => Some(self.fingerprint(secp).unwrap()), - _ => None, - }; - - Ok(Box::new(DescriptorExtendedKey { - master_fingerprint, - master_derivation, - pubkey, - secret: None, - path: path.into(), - final_index: self.final_index, - })) - } - - fn as_secret_key(&self) -> Option { - None - } - - fn xprv(&self) -> Option { - self.secret - } - - fn full_path(&self, index: u32) -> Option { - Some(self.full_path(index)) - } - - fn is_fixed(&self) -> bool { - self.final_index == DerivationIndex::Fixed - } -} diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 3000416b..103f9275 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -1,45 +1,275 @@ -use std::cell::RefCell; -use std::collections::BTreeMap; -use std::convert::{Into, TryFrom}; +use std::collections::{BTreeMap, HashMap}; use std::fmt; -use std::str::FromStr; +use std::sync::Arc; -use bitcoin::hashes::{hash160, Hash}; -use bitcoin::secp256k1::{All, Secp256k1}; -use bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey, Fingerprint}; -use bitcoin::util::psbt::PartiallySignedTransaction as PSBT; -use bitcoin::{PrivateKey, PublicKey, Script}; +use bitcoin::hashes::hash160; +use bitcoin::secp256k1::Secp256k1; +use bitcoin::util::bip32::{ChildNumber, DerivationPath, Fingerprint}; +use bitcoin::util::psbt; +use bitcoin::{PublicKey, Script, TxOut}; +use miniscript::descriptor::{DescriptorPublicKey, DescriptorXKey, InnerXKey}; pub use miniscript::{ - Descriptor, Legacy, Miniscript, MiniscriptKey, ScriptContext, Segwitv0, Terminal, + Descriptor, Legacy, Miniscript, MiniscriptKey, ScriptContext, Segwitv0, Terminal, ToPublicKey, }; -use serde::{Deserialize, Serialize}; - -use crate::psbt::utils::PSBTUtils; - pub mod checksum; pub mod error; -pub mod extended_key; -mod keys; pub mod policy; +// use crate::wallet::utils::AddressType; +use crate::wallet::signer::SignersContainer; + pub use self::checksum::get_checksum; use self::error::Error; -pub use self::extended_key::{DerivationIndex, DescriptorExtendedKey}; pub use self::policy::Policy; -use self::keys::Key; +pub type ExtendedDescriptor = Descriptor; +type HDKeyPaths = BTreeMap; -trait MiniscriptExtractPolicy { +pub trait ExtractPolicy { fn extract_policy( &self, - lookup_map: &BTreeMap>, + signers: Arc>, ) -> Result, Error>; } -pub trait ExtractPolicy { - fn extract_policy(&self) -> Result, Error>; +pub trait XKeyUtils { + fn full_path(&self, append: &[ChildNumber]) -> DerivationPath; + fn root_fingerprint(&self) -> Fingerprint; +} + +impl XKeyUtils for DescriptorXKey { + fn full_path(&self, append: &[ChildNumber]) -> DerivationPath { + let full_path = match &self.source { + &Some((_, ref path)) => path + .into_iter() + .chain(self.derivation_path.into_iter()) + .cloned() + .collect(), + &None => self.derivation_path.clone(), + }; + + if self.is_wildcard { + full_path + .into_iter() + .chain(append.into_iter()) + .cloned() + .collect() + } else { + full_path + } + } + + fn root_fingerprint(&self) -> Fingerprint { + match &self.source { + &Some((fingerprint, _)) => fingerprint.clone(), + &None => self.xkey.xkey_fingerprint(), + } + } +} + +pub trait DescriptorMeta: Sized { + fn is_witness(&self) -> bool; + fn get_hd_keypaths(&self, index: u32) -> Result; + fn is_fixed(&self) -> bool; + fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option; + fn derive_from_psbt_input(&self, psbt_input: &psbt::Input, utxo: Option) + -> Option; + // fn address_type(&self) -> Option; +} + +pub trait DescriptorScripts { + fn psbt_redeem_script(&self) -> Option