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/Cargo.toml b/integration_test/Cargo.toml index ba9c3aa5..f2b0ee09 100644 --- a/integration_test/Cargo.toml +++ b/integration_test/Cargo.toml @@ -5,6 +5,6 @@ authors = ["Steven Roose "] [dependencies] bitcoincore-rpc = { path = "../client" } -bitcoin = { version = "0.26", features = [ "use-serde", "rand" ] } +bitcoin = { version = "0.27", features = [ "use-serde", "rand" ] } lazy_static = "1.4.0" log = "0.4" 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 0e35ee79..aa39fb7d 100644 --- a/integration_test/src/main.rs +++ b/integration_test/src/main.rs @@ -17,6 +17,7 @@ extern crate lazy_static; extern crate log; use std::collections::HashMap; +use std::str::FromStr; use bitcoincore_rpc::json; use bitcoincore_rpc::jsonrpc::error::Error as JsonRpcError; @@ -125,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); @@ -203,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); } @@ -905,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![ @@ -914,6 +919,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: None, avoid_reuse: None, + descriptors: None, }, WalletParams { name: wallet_names[1], @@ -921,6 +927,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: None, avoid_reuse: None, + descriptors: None, }, WalletParams { name: wallet_names[2], @@ -928,6 +935,7 @@ fn test_create_wallet(cl: &Client) { blank: Some(true), passphrase: None, avoid_reuse: None, + descriptors: None, }, ]; @@ -938,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], @@ -945,6 +954,7 @@ fn test_create_wallet(cl: &Client) { blank: None, passphrase: None, avoid_reuse: Some(true), + descriptors: None, }); } @@ -956,6 +966,7 @@ fn test_create_wallet(cl: &Client) { wallet_param.blank, wallet_param.passphrase, wallet_param.avoid_reuse, + wallet_param.descriptors, ) .unwrap(); @@ -1050,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/Cargo.toml b/json/Cargo.toml index 25f28e1b..3ec82f60 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -21,4 +21,4 @@ path = "src/lib.rs" serde = { version = "1", features = [ "derive" ] } serde_json = "1" -bitcoin = { version = "0.26", features = [ "use-serde" ] } +bitcoin = { version = "0.27", features = [ "use-serde" ] } diff --git a/json/src/lib.rs b/json/src/lib.rs index 80dd4581..9309614a 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, +}