From a3df1c26b7568b620f1ddea17802bb15b6c76015 Mon Sep 17 00:00:00 2001 From: Sandipan Dey Date: Wed, 11 Aug 2021 21:44:48 +0530 Subject: [PATCH] added importdescriptors command, updated createwallet command with descriptors parameter and added bech32m address type --- client/src/client.rs | 16 +++++++++++-- integration_test/run.sh | 4 ++-- integration_test/src/main.rs | 45 ++++++++++++++++++++++++++++++------ json/src/lib.rs | 39 ++++++++++++++++++++++++++++++- 4 files changed, 92 insertions(+), 12 deletions(-) diff --git a/client/src/client.rs b/client/src/client.rs index 651acd96..3b258218 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -276,6 +276,7 @@ pub trait RpcApi: Sized { blank: Option, passphrase: Option<&str>, avoid_reuse: Option, + descriptors: Option, ) -> Result { let mut args = [ wallet.into(), @@ -283,13 +284,25 @@ pub trait RpcApi: Sized { opt_into_json(blank)?, opt_into_json(passphrase)?, opt_into_json(avoid_reuse)?, + opt_into_json(descriptors)?, ]; self.call( "createwallet", - handle_defaults(&mut args, &[false.into(), false.into(), into_json("")?, false.into()]), + handle_defaults( + &mut args, + &[false.into(), false.into(), into_json("")?, false.into(), false.into()], + ), ) } + fn import_descriptors( + &self, + descriptors: Vec, + ) -> Result> { + let arg = into_json(descriptors)?; + self.call("importdescriptors", &[arg]) + } + fn list_wallets(&self) -> Result> { self.call("listwallets", &[]) } @@ -1136,7 +1149,6 @@ impl RpcApi for Client { if log_enabled!(Debug) { debug!(target: "bitcoincore_rpc", "JSON-RPC request: {} {}", cmd, serde_json::Value::from(args)); } - let resp = self.client.send_request(req).map_err(Error::from); log_response(cmd, &resp); Ok(resp?.result()?) diff --git a/integration_test/run.sh b/integration_test/run.sh index b40aa78a..2c6c42a3 100755 --- a/integration_test/run.sh +++ b/integration_test/run.sh @@ -19,12 +19,12 @@ PID1=$! sleep 3 BLOCKFILTERARG="" -if bitcoind -version | grep -q "v0\.\(19\|2\)"; then +if bitcoind -version | grep -q "v\(0\.19\|0\.2\|2\)"; then BLOCKFILTERARG="-blockfilterindex=1" fi FALLBACKFEEARG="" -if bitcoind -version | grep -q "v0\.2"; then +if bitcoind -version | grep -q "v\(0\.2\|2\)"; then FALLBACKFEEARG="-fallbackfee=0.00001000" fi diff --git a/integration_test/src/main.rs b/integration_test/src/main.rs index 22f3cac2..3a06138c 100644 --- a/integration_test/src/main.rs +++ b/integration_test/src/main.rs @@ -19,6 +19,7 @@ extern crate log; use bitcoincore_rpc::core_rpc_json as bitcoincore_rpc_json; use std::collections::HashMap; +use std::str::FromStr; use bitcoincore_rpc::json; use bitcoincore_rpc::jsonrpc::error::Error as JsonRpcError; @@ -32,9 +33,7 @@ use bitcoin::{ Address, Amount, Network, OutPoint, PrivateKey, Script, SigHashType, SignedAmount, Transaction, TxIn, TxOut, Txid, }; -use bitcoincore_rpc_json::{ - GetBlockTemplateModes, GetBlockTemplateRules, ScanTxOutRequest, -}; +use bitcoincore_rpc_json::{GetBlockTemplateModes, GetBlockTemplateRules, ScanTxOutRequest}; lazy_static! { static ref SECP: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); @@ -127,15 +126,13 @@ fn main() { log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::max())).unwrap(); let rpc_url = format!("{}/wallet/testwallet", get_rpc_url()); - let auth = get_auth(); - - let cl = Client::new(&rpc_url, auth).unwrap(); + let cl = Client::new(&rpc_url, get_auth()).unwrap(); test_get_network_info(&cl); unsafe { VERSION = cl.version().unwrap() }; println!("Version: {}", version()); - cl.create_wallet("testwallet", None, None, None, None).unwrap(); + cl.create_wallet("testwallet", None, None, None, None, None).unwrap(); test_get_mining_info(&cl); test_get_blockchain_info(&cl); @@ -205,6 +202,11 @@ fn main() { //TODO load_wallet(&self, wallet: &str) -> Result { //TODO unload_wallet(&self, wallet: Option<&str>) -> Result<()> { //TODO backup_wallet(&self, destination: Option<&str>) -> Result<()> { + + let rpc_url = format!("{}/wallet/testdescriptorwallet", get_rpc_url()); + let desc_cl = Client::new(&rpc_url, get_auth()).unwrap(); + + test_descriptor_wallet(&desc_cl); test_stop(cl); } @@ -907,6 +909,7 @@ fn test_create_wallet(cl: &Client) { blank: Option, passphrase: Option<&'a str>, avoid_reuse: Option, + descriptors: Option, } let mut wallet_params = vec![ @@ -916,6 +919,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: None, avoid_reuse: None, + descriptors: None, }, WalletParams { name: wallet_names[1], @@ -923,6 +927,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: None, avoid_reuse: None, + descriptors: None, }, WalletParams { name: wallet_names[2], @@ -930,6 +935,7 @@ fn test_create_wallet(cl: &Client) { blank: Some(true), passphrase: None, avoid_reuse: None, + descriptors: None, }, ]; @@ -940,6 +946,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: Some("pass"), avoid_reuse: None, + descriptors: None, }); wallet_params.push(WalletParams { name: wallet_names[4], @@ -947,6 +954,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: None, avoid_reuse: Some(true), + descriptors: None, }); } @@ -958,6 +966,7 @@ fn test_create_wallet(cl: &Client) { wallet_param.blank, wallet_param.passphrase, wallet_param.avoid_reuse, + wallet_param.descriptors, ) .unwrap(); @@ -1052,3 +1061,25 @@ fn test_getblocktemplate(cl: &Client) { fn test_stop(cl: Client) { println!("Stopping: '{}'", cl.stop().unwrap()); } + +fn test_descriptor_wallet(cl: &Client) { + cl.create_wallet( + "testdescriptorwallet", + Some(false), + Some(true), + Some(""), + Some(false), + Some(true), + ) + .unwrap(); + + cl.import_descriptors( + vec![ + json::ImportDescriptorRequest::new("wpkh(tprv8ZgxMBicQKsPeRBCAfUGsZhyHy9dwWyPqhSJmaMnMJQWWtt8L2SkTeHaiF82CUCGtiTiHAs3cMkjdKckGKiCWeYtvMPF1jDTWYTryRMicpx/86h/1h/0h/0/*)#ymr4jlz6", false), + json::ImportDescriptorRequest::new("wpkh(tprv8ZgxMBicQKsPeRBCAfUGsZhyHy9dwWyPqhSJmaMnMJQWWtt8L2SkTeHaiF82CUCGtiTiHAs3cMkjdKckGKiCWeYtvMPF1jDTWYTryRMicpx/86h/1h/0h/1/*)#40x502jz", true), + ] + ).unwrap(); + + let add = cl.get_new_address(None, Some(json::AddressType::Bech32)).unwrap(); + assert_eq!(add, Address::from_str("bcrt1q7crcza94drr00skmu5x0n00rhmwnthde2frhwk").unwrap()); +} diff --git a/json/src/lib.rs b/json/src/lib.rs index ddde35cd..57e33a3e 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -1066,11 +1066,13 @@ pub struct GetPeerInfoResult { } #[derive(Copy, Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] -#[serde(rename_all = "lowercase")] +#[serde(rename_all = "snake_case")] pub enum GetPeerInfoResultNetwork { Ipv4, Ipv6, Onion, + I2p, + NotPubliclyRoutable, // this is undocumented upstream Unroutable, } @@ -1607,6 +1609,7 @@ pub enum AddressType { Legacy, P2shSegwit, Bech32, + Bech32m, } /// Used to represent arguments that can either be an address or a public key. @@ -1686,3 +1689,37 @@ where } Ok(Some(res)) } + +/// Import Descriptor Request +#[derive(Serialize, Clone, PartialEq, Eq, Debug)] +pub struct ImportDescriptorRequest { + pub active: bool, + #[serde(rename = "desc")] + pub descriptor: String, + pub range: [i64; 2], + pub next_index: i64, + pub timestamp: String, + pub internal: bool, +} + +impl ImportDescriptorRequest { + /// Create a new Import Descriptor request providing just the descriptor and internal flags + pub fn new(descriptor: &str, internal: bool) -> Self { + ImportDescriptorRequest { + descriptor: descriptor.to_string(), + internal, + active: true, + range: [0, 100], + next_index: 0, + timestamp: "now".to_string(), + } + } +} + +/// Imported Descriptor Result +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] +pub struct ImportDescriptorResult { + pub success: bool, + pub warnings: Option>, + pub error: Option, +}