Skip to content

Commit 96e084d

Browse files
committed
Merge #322: Address all TODO in v28
82b70b8 Run the formatter (Jamil Lambert, PhD) f8a832c Add createwalletdescriptor method and test (Jamil Lambert, PhD) 7f9c840 Add gethdkeys method model and test (Jamil Lambert, PhD) Pull request description: Go through all the TODO in the v28 types table. Add all the missing RPCs. - Add `gethdkeys` method, model and test.. - Add `createwalletdescriptor` method and test. This was labelled as model, but there is nothing to model. Update the types table to reflect this, `verify` was already correct and says no_model. - Run the formatter. ACKs for top commit: tcharding: ACK 82b70b8 Tree-SHA512: 7b40588df326c026c785f45ec76d39834724bdd9599ce21261b5b64e6c262495325134e7602edac5d530433fb0a0df3f07a55581b0bbfc678f2d4490507b60e6
2 parents d6873da + 82b70b8 commit 96e084d

File tree

11 files changed

+264
-17
lines changed

11 files changed

+264
-17
lines changed

client/src/client_sync/v28/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! We ignore option arguments unless they effect the shape of the returned JSON data.
66
77
pub mod raw_transactions;
8+
pub mod wallet;
89

910
use std::collections::BTreeMap;
1011
use std::path::Path;
@@ -141,13 +142,15 @@ crate::impl_client_v17__add_multisig_address!();
141142
crate::impl_client_v17__backup_wallet!();
142143
crate::impl_client_v17__bump_fee!();
143144
crate::impl_client_v23__create_wallet!();
145+
crate::impl_client_v28__create_wallet_descriptor!();
144146
crate::impl_client_v17__dump_priv_key!();
145147
crate::impl_client_v17__dump_wallet!();
146148
crate::impl_client_v17__encrypt_wallet!();
147149
crate::impl_client_v17__get_addresses_by_label!();
148150
crate::impl_client_v17__get_address_info!();
149151
crate::impl_client_v17__get_balance!();
150152
crate::impl_client_v19__get_balances!();
153+
crate::impl_client_v28__get_hd_keys!();
151154
crate::impl_client_v18__get_received_by_label!();
152155
crate::impl_client_v17__get_new_address!();
153156
crate::impl_client_v17__get_raw_change_address!();

client/src/client_sync/v28/wallet.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Macros for implementing JSON-RPC methods on a client.
4+
//!
5+
//! Specifically this is methods found under the `== Wallet ==` section of the
6+
//! API docs of Bitcoin Core `v28`.
7+
//!
8+
//! All macros require `Client` to be in scope.
9+
//!
10+
//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`.
11+
12+
/// Implements Bitcoin Core JSON-RPC API method `gethdkeys`.
13+
#[macro_export]
14+
macro_rules! impl_client_v28__get_hd_keys {
15+
() => {
16+
impl Client {
17+
pub fn get_hd_keys(&self) -> Result<GetHdKeys> { self.call("gethdkeys", &[]) }
18+
}
19+
};
20+
}
21+
22+
/// Implements Bitcoin Core JSON-RPC API method `createwalletdescriptor`.
23+
#[macro_export]
24+
macro_rules! impl_client_v28__create_wallet_descriptor {
25+
() => {
26+
impl Client {
27+
pub fn create_wallet_descriptor(&self, address_type: &str, hdkey: &str) -> Result<CreateWalletDescriptor> {
28+
let hdkey = serde_json::json!({ "hdkey": hdkey });
29+
self.call("createwalletdescriptor", &[address_type.into(), hdkey.into()])
30+
}
31+
}
32+
};
33+
}

client/src/client_sync/v29/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,15 @@ crate::impl_client_v17__add_multisig_address!();
142142
crate::impl_client_v17__backup_wallet!();
143143
crate::impl_client_v17__bump_fee!();
144144
crate::impl_client_v23__create_wallet!();
145+
crate::impl_client_v28__create_wallet_descriptor!();
145146
crate::impl_client_v17__dump_priv_key!();
146147
crate::impl_client_v17__dump_wallet!();
147148
crate::impl_client_v17__encrypt_wallet!();
148149
crate::impl_client_v17__get_addresses_by_label!();
149150
crate::impl_client_v17__get_address_info!();
150151
crate::impl_client_v17__get_balance!();
151152
crate::impl_client_v19__get_balances!();
153+
crate::impl_client_v28__get_hd_keys!();
152154
crate::impl_client_v18__get_received_by_label!();
153155
crate::impl_client_v17__get_new_address!();
154156
crate::impl_client_v17__get_raw_change_address!();

integration_test/tests/wallet.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![allow(unused_imports)] // Some imports are only used in specific versions.
77

88
use bitcoin::address::{Address, KnownHrp, NetworkChecked};
9+
use bitcoin::bip32::{Xpriv, Xpub};
910
use bitcoin::{secp256k1, Amount, CompressedPublicKey, FeeRate, Network, PrivateKey, PublicKey};
1011
use integration_test::{Node, NodeExt as _, Wallet};
1112
use node::{mtype, AddressType, ImportMultiRequest, ImportMultiScriptPubKey, ImportMultiTimestamp};
@@ -118,6 +119,40 @@ fn wallet__create_wallet__modelled() {
118119
let _ = Node::with_wallet(Wallet::Default, &[]);
119120
}
120121

122+
#[test]
123+
#[cfg(not(feature = "v27_and_below"))]
124+
fn wallet__create_wallet_descriptor() {
125+
let node = Node::with_wallet(Wallet::Default, &[]);
126+
127+
// BIP32 HD xprv/xpub for the creation of a descriptor with a private key that is in the wallet.
128+
let secp = secp256k1::Secp256k1::new();
129+
let seed = [0u8; 32];
130+
let xprv = Xpriv::new_master(Network::Regtest, &seed).unwrap();
131+
let xpub = Xpub::from_priv(&secp, &xprv);
132+
let hdkey = xpub.to_string();
133+
134+
// Import the private key into the wallet.
135+
let privkey = bitcoin::PrivateKey {
136+
compressed: true,
137+
network: Network::Regtest.into(),
138+
inner: xprv.private_key,
139+
};
140+
let wif = privkey.to_wif();
141+
let raw_descriptor = format!("wpkh({})", wif);
142+
let info = node.client.get_descriptor_info(&raw_descriptor).expect("get_descriptor_info");
143+
let descriptor = format!("{}#{}", raw_descriptor, info.checksum);
144+
145+
let import_req = ImportDescriptorsRequest::new(descriptor, 0);
146+
node.client.import_descriptors(&[import_req]).expect("importdescriptors");
147+
148+
let json = node.client.create_wallet_descriptor("bech32", &hdkey)
149+
.expect("createwalletdescriptor");
150+
151+
// Check that a SigWit descriptor was created.
152+
let prefix = &json.descriptors[0][0..4];
153+
assert_eq!(prefix, "wpkh");
154+
}
155+
121156
#[test]
122157
fn wallet__dump_priv_key__modelled() {
123158
// As of Core v23 the default wallet is an native descriptor wallet which does not
@@ -247,6 +282,19 @@ fn wallet__get_balances() {
247282
model.unwrap();
248283
}
249284

285+
#[test]
286+
#[cfg(not(feature = "v27_and_below"))]
287+
fn wallet__get_hd_keys__modelled() {
288+
let node = Node::with_wallet(Wallet::Default, &[]);
289+
290+
let json: GetHdKeys = node.client.get_hd_keys().expect("gethdkeys");
291+
let model: Result<mtype::GetHdKeys, _> = json.into_model();
292+
let hdkey = model.unwrap().0;
293+
294+
let descriptor_type = hdkey[0].descriptors[0].descriptor[..3].to_string();
295+
assert_eq!(descriptor_type, "pkh");
296+
}
297+
250298
#[test]
251299
fn wallet__get_new_address__modelled() {
252300
let node = Node::with_wallet(Wallet::Default, &[]);

types/src/model/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ pub use self::{
5353
wallet::{
5454
AddMultisigAddress, AddressInformation, AddressPurpose, Bip125Replaceable, BumpFee,
5555
CreateWallet, DumpPrivKey, GetAddressInfo, GetAddressInfoEmbedded, GetAddressesByLabel,
56-
GetBalance, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetNewAddress,
56+
GetBalance, GetBalances, GetBalancesMine, GetBalancesWatchOnly, GetHdKeys, GetNewAddress,
5757
GetRawChangeAddress, GetReceivedByAddress, GetReceivedByLabel, GetTransaction,
58-
GetTransactionDetail, GetUnconfirmedBalance, GetWalletInfo, LastProcessedBlock,
59-
ListAddressGroupings, ListAddressGroupingsItem, ListLabels, ListLockUnspent,
60-
ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem, ListReceivedByLabel,
61-
ListReceivedByLabelItem, ListSinceBlock, ListSinceBlockTransaction, ListTransactions,
62-
ListTransactionsItem, ListUnspent, ListUnspentItem, ListWallets, LoadWallet, PsbtBumpFee,
63-
RescanBlockchain, ScriptType, Send, SendAll, SendMany, SendToAddress, SignMessage,
64-
SimulateRawTransaction, TransactionCategory, UnloadWallet, WalletCreateFundedPsbt,
65-
WalletDisplayAddress, WalletProcessPsbt,
58+
GetTransactionDetail, GetUnconfirmedBalance, GetWalletInfo, HdKey, HdKeyDescriptor,
59+
LastProcessedBlock, ListAddressGroupings, ListAddressGroupingsItem, ListLabels,
60+
ListLockUnspent, ListLockUnspentItem, ListReceivedByAddress, ListReceivedByAddressItem,
61+
ListReceivedByLabel, ListReceivedByLabelItem, ListSinceBlock, ListSinceBlockTransaction,
62+
ListTransactions, ListTransactionsItem, ListUnspent, ListUnspentItem, ListWallets,
63+
LoadWallet, PsbtBumpFee, RescanBlockchain, ScriptType, Send, SendAll, SendMany,
64+
SendToAddress, SignMessage, SimulateRawTransaction, TransactionCategory, UnloadWallet,
65+
WalletCreateFundedPsbt, WalletDisplayAddress, WalletProcessPsbt,
6666
},
6767
};

types/src/model/wallet.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use alloc::collections::BTreeMap;
99

1010
use bitcoin::address::NetworkUnchecked;
11+
use bitcoin::bip32::{Xpriv, Xpub};
1112
use bitcoin::hashes::hash160;
1213
use bitcoin::{
1314
bip32, sign_message, Address, Amount, BlockHash, FeeRate, PrivateKey, Psbt, PublicKey,
@@ -284,6 +285,35 @@ pub struct GetBalancesWatchOnly {
284285
pub immature: Amount,
285286
}
286287

288+
/// Models the result of JSON-RPC method `gethdkeys`.
289+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
290+
#[serde(deny_unknown_fields)]
291+
pub struct GetHdKeys(pub Vec<HdKey>);
292+
293+
/// An HD key entry returned by `gethdkeys`.
294+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
295+
#[serde(deny_unknown_fields)]
296+
pub struct HdKey {
297+
/// The extended public key.
298+
pub xpub: Xpub,
299+
/// Whether the wallet has the private key for this xpub.
300+
pub has_private: bool,
301+
/// The extended private key if "private" is true.
302+
pub xpriv: Option<Xpriv>,
303+
/// Array of descriptor objects that use this HD key.
304+
pub descriptors: Vec<HdKeyDescriptor>,
305+
}
306+
307+
/// Descriptor object used in `gethdkeys` result.
308+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
309+
#[serde(deny_unknown_fields)]
310+
pub struct HdKeyDescriptor {
311+
/// Descriptor string representation.
312+
pub descriptor: String,
313+
/// Whether this descriptor is currently used to generate new addresses.
314+
pub active: bool,
315+
}
316+
287317
/// Models the result of JSON-RPC method `getnewaddress`.
288318
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
289319
#[serde(deny_unknown_fields)]

types/src/v28/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,15 @@
176176
//! | backupwallet | returns nothing | |
177177
//! | bumpfee | version + model | |
178178
//! | createwallet | version + model | |
179-
//! | createwalletdescriptor | version + model | TODO |
179+
//! | createwalletdescriptor | version | |
180180
//! | dumpprivkey | version + model | |
181181
//! | dumpwallet | version + model | |
182182
//! | encryptwallet | version | |
183183
//! | getaddressesbylabel | version + model | |
184184
//! | getaddressinfo | version + model | |
185185
//! | getbalance | version + model | |
186186
//! | getbalances | version + model | |
187-
//! | gethdkeys | version + model | TODO |
187+
//! | gethdkeys | version + model | |
188188
//! | getnewaddress | version + model | |
189189
//! | getrawchangeaddress | version + model | |
190190
//! | getreceivedbyaddress | version + model | |
@@ -267,7 +267,10 @@ pub use self::{
267267
SubmitPackage, SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError,
268268
SubmitPackageTxResultFees, SubmitPackageTxResultFeesError,
269269
},
270-
wallet::{GetAddressInfo, GetAddressInfoEmbedded, GetTransaction},
270+
wallet::{
271+
CreateWalletDescriptor, GetAddressInfo, GetAddressInfoEmbedded, GetHdKeys, GetHdKeysError,
272+
GetTransaction, HdKey, HdKeyDescriptor,
273+
},
271274
};
272275
#[doc(inline)]
273276
pub use crate::{

types/src/v28/wallet/error.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
use core::fmt;
4+
5+
use bitcoin::bip32;
6+
7+
use crate::error::write_err;
8+
use crate::NumericError;
9+
10+
/// Error when converting a `GetHdKeys` type into the model type.
11+
#[derive(Debug)]
12+
pub enum GetHdKeysError {
13+
/// Conversion of the `xpub` field failed.
14+
Xpub(bip32::Error),
15+
/// Conversion of the `xpriv` field failed.
16+
Xpriv(bip32::Error),
17+
/// Conversion of numeric type to expected type failed.
18+
Numeric(NumericError),
19+
}
20+
21+
impl fmt::Display for GetHdKeysError {
22+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23+
use GetHdKeysError::*;
24+
match *self {
25+
Xpub(ref e) => write_err!(f, "conversion of the `xpub` field failed"; e),
26+
Xpriv(ref e) => write_err!(f, "conversion of the `xpriv` field failed"; e),
27+
Numeric(ref e) => write_err!(f, "numeric"; e),
28+
}
29+
}
30+
}
31+
32+
#[cfg(feature = "std")]
33+
impl std::error::Error for GetHdKeysError {
34+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
35+
use GetHdKeysError::*;
36+
match *self {
37+
Xpub(ref e) => Some(e),
38+
Xpriv(ref e) => Some(e),
39+
Numeric(ref e) => Some(e),
40+
}
41+
}
42+
}
43+
44+
impl From<NumericError> for GetHdKeysError {
45+
fn from(e: NumericError) -> Self { Self::Numeric(e) }
46+
}

types/src/v28/wallet/into.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bitcoin::{
1111

1212
use super::{
1313
GetAddressInfo, GetAddressInfoEmbedded, GetAddressInfoEmbeddedError, GetAddressInfoError,
14-
GetTransaction, GetTransactionError,
14+
GetHdKeys, GetHdKeysError, GetTransaction, GetTransactionError,
1515
};
1616
use crate::model;
1717

@@ -155,6 +155,33 @@ impl GetAddressInfoEmbedded {
155155
}
156156
}
157157

158+
impl GetHdKeys {
159+
/// Converts version specific type to a version nonspecific, more strongly typed type.
160+
pub fn into_model(self) -> Result<model::GetHdKeys, GetHdKeysError> {
161+
let keys = self
162+
.0
163+
.into_iter()
164+
.map(|item| {
165+
let xpub = item.xpub.parse().map_err(GetHdKeysError::Xpub)?;
166+
let xpriv = match item.xpriv {
167+
Some(xpriv) => Some(xpriv.parse().map_err(GetHdKeysError::Xpriv)?),
168+
None => None,
169+
};
170+
let descriptors = item
171+
.descriptors
172+
.into_iter()
173+
.map(|desc| model::HdKeyDescriptor {
174+
descriptor: desc.descriptor,
175+
active: desc.active,
176+
})
177+
.collect();
178+
Ok(model::HdKey { xpub, has_private: item.has_private, xpriv, descriptors })
179+
})
180+
.collect::<Result<Vec<_>, GetHdKeysError>>()?;
181+
Ok(model::GetHdKeys(keys))
182+
}
183+
}
184+
158185
impl GetTransaction {
159186
/// Converts version specific type to a version nonspecific, more strongly typed type.
160187
pub fn into_model(self) -> Result<model::GetTransaction, GetTransactionError> {

0 commit comments

Comments
 (0)