Skip to content

Commit 9240ce0

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 0eb4a9c commit 9240ce0

File tree

1 file changed

+160
-3
lines changed

1 file changed

+160
-3
lines changed

wallet/src/test_utils.rs

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

79
use bdk_chain::{BlockId, ConfirmationBlockTime, TxUpdate};
810
use bitcoin::{
9-
absolute, hashes::Hash, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint,
10-
Transaction, TxIn, TxOut, Txid,
11+
absolute,
12+
hashes::Hash,
13+
key::Secp256k1,
14+
psbt::{GetKey, GetKeyError, KeyRequest},
15+
transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, Transaction, TxIn, TxOut,
16+
Txid,
1117
};
1218

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

15172
/// Return a fake wallet that appears to be funded for testing.
16173
///

0 commit comments

Comments
 (0)