diff --git a/client/src/client_sync/v29/mod.rs b/client/src/client_sync/v29/mod.rs index db244550..6969b60c 100644 --- a/client/src/client_sync/v29/mod.rs +++ b/client/src/client_sync/v29/mod.rs @@ -5,6 +5,7 @@ //! We ignore option arguments unless they effect the shape of the returned JSON data. pub mod blockchain; +pub mod util; use std::collections::BTreeMap; use std::path::Path; @@ -126,7 +127,7 @@ crate::impl_client_v22__enumerate_signers!(); // == Util == crate::impl_client_v17__create_multisig!(); -crate::impl_client_v18__derive_addresses!(); +crate::impl_client_v29__derive_addresses!(); crate::impl_client_v17__estimate_smart_fee!(); crate::impl_client_v18__get_descriptor_info!(); crate::impl_client_v21__get_index_info!(); diff --git a/client/src/client_sync/v29/util.rs b/client/src/client_sync/v29/util.rs new file mode 100644 index 00000000..87ed5f25 --- /dev/null +++ b/client/src/client_sync/v29/util.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! Macros for implementing JSON-RPC methods on a client. +//! +//! Specifically this is methods found under the `== Util ==` section of the +//! API docs of Bitcoin Core `v29`. +//! +//! All macros require `Client` to be in scope. +//! +//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`. + +/// Implements Bitcoin Core JSON-RPC API method `deriveaddresses`. +#[macro_export] +macro_rules! impl_client_v29__derive_addresses { + () => { + impl Client { + // For single derivation descriptors. + pub fn derive_addresses(&self, descriptor: &str) -> Result { + self.call("deriveaddresses", &[descriptor.into()]) + } + + // For multipath descriptors. + pub fn derive_addresses_multipath( + &self, + descriptor: &str, + range: (u32, u32), + ) -> Result { + let range = json!([range.0, range.1]); + self.call("deriveaddresses", &[descriptor.into(), range.into()]) + } + } + }; +} diff --git a/integration_test/tests/util.rs b/integration_test/tests/util.rs index 66cf1019..2a6b09f4 100644 --- a/integration_test/tests/util.rs +++ b/integration_test/tests/util.rs @@ -41,6 +41,24 @@ fn util__derive_addresses__modelled() { let json: DeriveAddresses = node.client.derive_addresses(descriptor).expect("deriveaddresses"); let res: Result = json.into_model(); let _ = res.expect("DeriveAddresses into model"); + + // For v29 and above test a multipath descriptor. + #[cfg(not(feature = "v28_and_below"))] + { + // Create a multipath descriptor taken from running `listdescriptors` on the node. With 2 derivation paths. + let multipath_descriptor = "wpkh([26b4ed16/84h/1h/0h]tpubDDe7JUw2CGU1rYZxupmNrhDXuE1fv25gs4je3BBuWCFwTW9QHGgyh5cjAEugd14ysJXTVshPvnUVABfD66HZKCS9gp5AYFd5K2WN2oVFp8t/<0;1>/*)#grvmsm8m"; + + let range = (0, 3); + let json: DeriveAddressesMultipath = node.client.derive_addresses_multipath(multipath_descriptor, range) + .expect("deriveaddresses"); + let res: Result = json.into_model(); + let derived = res.expect("DeriveAddressesMultipath into model"); + + // Should return 2 `DeriveAddresses`, one for each derivation path (0 and 1). + assert_eq!(derived.addresses.len(), 2); + // Each `DeriveAddresses` should contain 4 addresses for range [0, 3]. + assert_eq!(derived.addresses[0].addresses.len(), 4); + } } #[test] diff --git a/types/src/model/mod.rs b/types/src/model/mod.rs index 4ae73829..80c3c89a 100644 --- a/types/src/model/mod.rs +++ b/types/src/model/mod.rs @@ -47,7 +47,8 @@ pub use self::{ TestMempoolAccept, UtxoUpdatePsbt, }, util::{ - CreateMultisig, DeriveAddresses, EstimateSmartFee, SignMessageWithPrivKey, ValidateAddress, + CreateMultisig, DeriveAddresses, DeriveAddressesMultipath, EstimateSmartFee, + SignMessageWithPrivKey, ValidateAddress, }, wallet::{ AddMultisigAddress, AddressInformation, AddressLabel, AddressPurpose, Bip125Replaceable, diff --git a/types/src/model/util.rs b/types/src/model/util.rs index fb5664ff..97555fb7 100644 --- a/types/src/model/util.rs +++ b/types/src/model/util.rs @@ -48,6 +48,14 @@ pub struct DeriveAddresses { pub addresses: Vec>, } +/// Models the result of JSON-RPC method `deriveaddresses` for multipath descriptors. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct DeriveAddressesMultipath { + /// The derived addresses for each of the multipath expansions of the descriptor, in multipath specifier order. + pub addresses: Vec, +} + /// Models the result of JSON-RPC method `signmessagewithprivkey`. #[derive(Clone, Debug, PartialEq, Eq)] pub struct SignMessageWithPrivKey(pub sign_message::MessageSignature); diff --git a/types/src/v29/mod.rs b/types/src/v29/mod.rs index 158ac36e..94973f2d 100644 --- a/types/src/v29/mod.rs +++ b/types/src/v29/mod.rs @@ -156,7 +156,7 @@ //! | JSON-RPC Method Name | Returns | Notes | //! |:-----------------------------------|:---------------:|:--------------------------------------:| //! | createmultisig | version + model | | -//! | deriveaddresses | version + model | TODO | +//! | deriveaddresses | version + model | | //! | estimatesmartfee | version + model | | //! | getdescriptorinfo | version | | //! | getindexinfo | version | | @@ -267,7 +267,7 @@ pub use self::{ BlockTemplateTransaction, GetMiningInfo, GetMiningInfoError, NextBlockInfo, NextBlockInfoError, }, - util::GetDescriptorInfo, + util::{DeriveAddressesMultipath, GetDescriptorInfo}, }; #[doc(inline)] pub use crate::{ diff --git a/types/src/v29/util.rs b/types/src/v29/util.rs index e1c8a98e..1d2efffb 100644 --- a/types/src/v29/util.rs +++ b/types/src/v29/util.rs @@ -4,8 +4,34 @@ //! //! Types for methods found under the `== Util ==` section of the API docs. +use bitcoin::address; use serde::{Deserialize, Serialize}; +use super::DeriveAddresses; +use crate::model; + +/// Result of JSON-RPC method `deriveaddresses` for multipath descriptors. +/// +/// > deriveaddresses "descriptor" ( range ) +/// > +/// > Derives one or more addresses corresponding to an output descriptor. +/// > Returns an array of derived addresses. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct DeriveAddressesMultipath(pub Vec); + +impl DeriveAddressesMultipath { + /// Converts version specific type to a version nonspecific, more strongly typed type. + pub fn into_model(self) -> Result { + let derive_addresses = self + .0 + .into_iter() + .map(|derive_addresses| derive_addresses.into_model()) + .collect::, _>>()?; + Ok(model::DeriveAddressesMultipath { addresses: derive_addresses }) + } +} + /// Result of JSON-RPC method `getdescriptorinfo`. /// /// > getdescriptorinfo "descriptor"