Skip to content

Commit c3b889b

Browse files
committed
feat(signer): add new SignerWrapper for KeyMap
- adds a new `SignerWrapper` for `KeyMap` as a test utility, with new methods: `get_wallet_signer` and `get_wallet_signer_single`. - adds an implementation of `GetKey` trait for `SignerWrapper`, in order to retrieve the private key for software signers, and successfully use `Psbt::sign` method. - the new methods added are necessary, in order to update the existing tests and examples to use `Psbt::sign` instead of `Wallet::sign`, as it further allows the signing APIs and related types to be fully removed.
1 parent 585e196 commit c3b889b

File tree

1 file changed

+143
-3
lines changed

1 file changed

+143
-3
lines changed

wallet/src/test_utils.rs

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,154 @@
33
use alloc::string::ToString;
44
use alloc::sync::Arc;
55
use core::str::FromStr;
6+
use miniscript::{
7+
descriptor::{DescriptorSecretKey, KeyMap},
8+
DescriptorPublicKey,
9+
};
10+
use std::collections::BTreeMap;
611

712
use bdk_chain::{BlockId, ConfirmationBlockTime, TxUpdate};
813
use bitcoin::{
9-
absolute, hashes::Hash, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint,
10-
Transaction, TxIn, TxOut, Txid,
14+
absolute,
15+
hashes::Hash,
16+
key::Secp256k1,
17+
psbt::{GetKey, GetKeyError, KeyRequest},
18+
transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, Transaction, TxIn, TxOut,
19+
Txid,
1120
};
1221

13-
use crate::{KeychainKind, Update, Wallet};
22+
use crate::{descriptor::check_wallet_descriptor, KeychainKind, Update, Wallet};
23+
24+
#[derive(Debug, Clone)]
25+
/// A wrapper over the [`KeyMap`] type that has the `GetKey` trait implementation for signing.
26+
pub struct SignerWrapper {
27+
key_map: KeyMap,
28+
}
29+
30+
impl SignerWrapper {
31+
/// Creates a new [`SignerWrapper`] for the given [`KeyMap`].
32+
pub fn new(key_map: KeyMap) -> Self {
33+
Self { key_map }
34+
}
35+
}
36+
37+
impl GetKey for SignerWrapper {
38+
type Error = GetKeyError;
39+
40+
fn get_key<C: bitcoin::secp256k1::Signing>(
41+
&self,
42+
key_request: KeyRequest,
43+
secp: &bitcoin::key::Secp256k1<C>,
44+
) -> Result<Option<bitcoin::PrivateKey>, Self::Error> {
45+
for (descriptor_pk, descriptor_sk) in self.key_map.iter() {
46+
match (descriptor_sk, key_request) {
47+
(DescriptorSecretKey::Single(single_priv), key_request) => {
48+
let private_key = single_priv.key;
49+
let public_key = private_key.public_key(secp);
50+
let keys = BTreeMap::from([(public_key, private_key)]);
51+
return keys.get_key(key_request, secp);
52+
}
53+
(DescriptorSecretKey::XPrv(descriptor_xkey), KeyRequest::Pubkey(public_key)) => {
54+
match descriptor_pk {
55+
DescriptorPublicKey::XPub(descriptor_xpub) => {
56+
let xpub = descriptor_xpub.xkey;
57+
match xpub.public_key.eq(&public_key.inner) {
58+
true => {
59+
return Ok(Some(
60+
descriptor_xkey
61+
.xkey
62+
.derive_priv(secp, &descriptor_xkey.derivation_path)?
63+
.to_priv(),
64+
))
65+
}
66+
false => return Ok(None),
67+
}
68+
}
69+
_ => unreachable!(),
70+
}
71+
}
72+
(
73+
DescriptorSecretKey::XPrv(descriptor_xkey),
74+
ref key_request @ KeyRequest::Bip32(ref key_source),
75+
) => {
76+
let xpriv = descriptor_xkey.xkey;
77+
match xpriv.get_key(key_request.clone(), secp) {
78+
Ok(Some(private_key)) => return Ok(Some(private_key)),
79+
Ok(None) => match descriptor_xkey.matches(key_source, secp) {
80+
Some(_derivation_path) => match &descriptor_xkey.origin {
81+
Some((_origin_fingerprint, origin_derivation_path)) => {
82+
let (_fingerprint, derivation_path) = key_source;
83+
let derivation_path =
84+
&derivation_path[origin_derivation_path.len()..];
85+
return Ok(Some(
86+
descriptor_xkey
87+
.xkey
88+
.derive_priv(secp, &derivation_path)?
89+
.to_priv(),
90+
));
91+
}
92+
None => {
93+
let (_fingerprint, derivation_path) = key_source;
94+
return Ok(Some(
95+
descriptor_xkey
96+
.xkey
97+
.derive_priv(secp, &derivation_path)?
98+
.to_priv(),
99+
));
100+
}
101+
},
102+
None => return Ok(None),
103+
},
104+
Err(e) => return Err(e),
105+
}
106+
}
107+
(
108+
DescriptorSecretKey::XPrv(_descriptor_xkey),
109+
KeyRequest::XOnlyPubkey(_xonly_public_key),
110+
) => return Err(GetKeyError::NotSupported), // TODO: (@leonardo) should we handle this ?
111+
(DescriptorSecretKey::MultiXPrv(_descriptor_multi_xkey), _) => unimplemented!(),
112+
_ => unreachable!(),
113+
}
114+
}
115+
Ok(None)
116+
}
117+
}
118+
119+
120+
/// Create the [`CreateParams`] for the provided testing `descriptor` and `change_descriptor`.
121+
pub fn get_wallet_params(descriptor: &str, change_descriptor: Option<&str>) -> crate::CreateParams {
122+
if let Some(change_desc) = change_descriptor {
123+
Wallet::create(descriptor.to_string(), change_desc.to_string())
124+
} else {
125+
Wallet::create_single(descriptor.to_string())
126+
}
127+
}
128+
129+
/// Create a new [`SignerWrapper`] for the provided testing `descriptor` and `change_descriptor`.
130+
pub fn get_wallet_signer(descriptor: &str, change_descriptor: Option<&str>) -> SignerWrapper {
131+
let secp = Secp256k1::new();
132+
let params = get_wallet_params(descriptor, change_descriptor).network(Network::Regtest);
133+
134+
let network = params.network;
135+
136+
let (descriptor, mut descriptor_keymap) = (params.descriptor)(&secp, network).unwrap();
137+
check_wallet_descriptor(&descriptor).unwrap();
138+
descriptor_keymap.extend(params.descriptor_keymap);
139+
140+
if let Some(change_descriptor) = params.change_descriptor {
141+
let (change_descriptor, mut change_keymap) = change_descriptor(&secp, network).unwrap();
142+
check_wallet_descriptor(&change_descriptor).unwrap();
143+
change_keymap.extend(params.change_descriptor_keymap);
144+
descriptor_keymap.extend(change_keymap)
145+
}
146+
147+
SignerWrapper::new(descriptor_keymap)
148+
}
149+
150+
/// Create a new [`SignerWrapper`] for the provided testing `descriptor`.
151+
pub fn get_wallet_signer_single(descriptor: &str) -> SignerWrapper {
152+
get_wallet_signer(descriptor, None)
153+
}
14154

15155
/// Return a fake wallet that appears to be funded for testing.
16156
///

0 commit comments

Comments
 (0)