From 72f8f0b907c80b54603f9f55468c1ee88f176635 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 00:03:40 +0100 Subject: [PATCH 01/19] some progress --- Cargo.lock | 16 ++++++++--- Cargo.toml | 3 +- src/subcommand/wallet/create.rs | 44 ++++++++++++++++++++++++++++-- src/subcommand/wallet/inscribe.rs | 29 +++++++------------- test-bitcoincore-rpc/Cargo.toml | 4 +-- test-bitcoincore-rpc/src/api.rs | 4 +-- test-bitcoincore-rpc/src/lib.rs | 6 ++-- test-bitcoincore-rpc/src/server.rs | 17 ++++++++---- test-bitcoincore-rpc/src/state.rs | 4 +-- tests/wallet.rs | 25 ++++------------- tests/wallet/create.rs | 30 ++++++++++++++++++++ 11 files changed, 123 insertions(+), 59 deletions(-) create mode 100644 tests/wallet/create.rs diff --git a/Cargo.lock b/Cargo.lock index a9366b9c35..2d04c60f0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1715,6 +1715,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniscript" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123a10aae81d0712ecc09b780f6f0ae0b0f506a5c4c912974725760d59ba073e" +dependencies = [ + "bitcoin", +] + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1943,6 +1952,7 @@ dependencies = [ "log", "mime", "mime_guess", + "miniscript", "ord-bitcoincore-rpc", "pretty_assertions", "pulldown-cmark", @@ -1967,8 +1977,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc" version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e050561c1c3084a9eac260b2100a3751101886d302068a11c1facd8a67bade9" +source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#7028a3030f0ce7ba37f51b2444567924f2460d6d" dependencies = [ "jsonrpc", "log", @@ -1980,8 +1989,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc-json" version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279b49e6ebf101f3446f32f58879b82d0a564b37f0eca01d2169901c250038de" +source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#7028a3030f0ce7ba37f51b2444567924f2460d6d" dependencies = [ "bitcoin", "serde", diff --git a/Cargo.toml b/Cargo.toml index 08204ec330..d4ab53a312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,8 @@ lazy_static = "1.4.0" log = "0.4.14" mime = "0.3.16" mime_guess = "2.0.4" -ord-bitcoincore-rpc = "0.16.0" +miniscript = "9.0.0" +ord-bitcoincore-rpc = { git = "https://github.com/casey/rust-bitcoincore-rpc", branch = "ord" } redb = "0.11.0" regex = "1.6.0" rust-embed = "6.4.0" diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 323997e048..3320756b8e 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -1,8 +1,48 @@ -use super::*; +use { + super::*, + bitcoin::secp256k1::rand::RngCore, + bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey}, + bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, + miniscript::descriptor::{ + Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard, + }, +}; pub(crate) fn run(options: Options) -> Result { + // TODO: check for correct bitcoin core version + + options + .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? + .create_wallet("ord", None, Some(true), None, None)?; + + let mut seed = [0; 32]; + bitcoin::secp256k1::rand::thread_rng().fill_bytes(&mut seed); + + let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { + origin: None, + xkey: ExtendedPrivKey::new_master(options.chain().network(), &seed)?, + derivation_path: DerivationPath::master(), + wildcard: Wildcard::None, + }); + + let public_key = secret_key.to_public(&bitcoin::secp256k1::Secp256k1::new())?; + + let mut key_map = std::collections::HashMap::new(); + key_map.insert(public_key.clone(), secret_key); + + let desc = Descriptor::new_tr(public_key, None)?; + options .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? - .create_wallet("ord", None, None, None, None)?; + .import_descriptors(ImportDescriptors { + descriptor: desc.to_string_with_secret(&key_map), + timestamp: Timestamp::Now, + active: Some(true), + range: None, + next_index: None, + internal: None, + label: None, + })?; + Ok(()) } diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 58d6531997..c9b2b12b45 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -11,8 +11,8 @@ use { util::taproot::{LeafVersion, TapLeafHash, TaprootBuilder}, PackedLockTime, SchnorrSighashType, Witness, }, + bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, bitcoincore_rpc::Client, - serde_json::json, std::collections::BTreeSet, }; @@ -260,24 +260,15 @@ impl Inscribe { let info = client.get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; - let params = json!([ - { - "desc": format!("rawtr({})#{}", recovery_private_key.to_wif(), info.checksum), - "active": false, - "timestamp": "now", - "internal": false, - "label": format!("commit tx recovery key") - } - ]); - - #[derive(Deserialize)] - struct ImportDescriptorsResult { - success: bool, - } - - let response: Vec = client - .call("importdescriptors", &[params]) - .context("could not import commit tx recovery key")?; + let response = client.import_descriptors(ImportDescriptors { + descriptor: format!("rawtr({})#{}", recovery_private_key.to_wif(), info.checksum), + timestamp: Timestamp::Now, + active: Some(false), + range: None, + next_index: None, + internal: Some(false), + label: Some(format!("commit tx recovery key")), + })?; for result in response { if !result.success { diff --git a/test-bitcoincore-rpc/Cargo.toml b/test-bitcoincore-rpc/Cargo.toml index 4a45dd472f..d318f37c19 100644 --- a/test-bitcoincore-rpc/Cargo.toml +++ b/test-bitcoincore-rpc/Cargo.toml @@ -8,12 +8,12 @@ homepage = "https://github.com/casey/ord" repository = "https://github.com/casey/ord" [dependencies] -bitcoin = { version = "0.29.1", features = ["serde"] } +bitcoin = { version = "0.29.1", features = ["serde", "rand"] } hex = "0.4.3" jsonrpc-core = "18.0.0" jsonrpc-derive = "18.0.0" jsonrpc-http-server = "18.0.0" -ord-bitcoincore-rpc = "0.16.0" +ord-bitcoincore-rpc = { git = "https://github.com/casey/rust-bitcoincore-rpc", branch = "ord" } reqwest = { version = "0.11.10", features = ["blocking"] } serde = { version = "1", features = ["derive"] } serde_json = "1.0.0" diff --git a/test-bitcoincore-rpc/src/api.rs b/test-bitcoincore-rpc/src/api.rs index 3ad49dce8d..c329229c87 100644 --- a/test-bitcoincore-rpc/src/api.rs +++ b/test-bitcoincore-rpc/src/api.rs @@ -114,8 +114,8 @@ pub trait Api { #[rpc(name = "importdescriptors")] fn import_descriptors( &self, - params: Vec, - ) -> Result; + req: Vec, + ) -> Result, jsonrpc_core::Error>; #[rpc(name = "getnewaddress")] fn get_new_address( diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index 2fe6129af8..6539c2b00b 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -18,7 +18,7 @@ use { GetNetworkInfoResult, GetRawTransactionResult, GetTransactionResult, GetTransactionResultDetail, GetTransactionResultDetailCategory, GetWalletInfoResult, ListTransactionResult, ListUnspentResultEntry, LoadWalletResult, SignRawTransactionResult, - WalletTxInfo, + WalletTxInfo, ImportDescriptors, ImportMultiResult, }, jsonrpc_core::{IoHandler, Value}, jsonrpc_http_server::{CloseHandle, ServerBuilder}, @@ -211,8 +211,8 @@ impl Handle { self.state().mempool().to_vec() } - pub fn descriptors(&self) -> u64 { - self.state().descriptors + pub fn descriptors(&self) -> Vec { + self.state().descriptors.clone() } pub fn sent(&self) -> Vec { diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 02cf1fdd66..06fd2e4f38 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -4,7 +4,6 @@ use { secp256k1::{rand, KeyPair, Secp256k1, XOnlyPublicKey}, Address, Witness, }, - serde_json::json, }; pub(crate) struct Server { @@ -439,10 +438,18 @@ impl Api for Server { fn import_descriptors( &self, - _params: Vec, - ) -> Result { - self.state().descriptors += 1; - Ok(json!([{"success": true}])) + req: Vec, + ) -> Result, jsonrpc_core::Error> { + self + .state() + .descriptors + .extend(req.into_iter().map(|params| String::from(params.descriptor))); + + Ok(vec![ImportMultiResult { + success: true, + warnings: Vec::new(), + error: None, + }]) } fn get_new_address( diff --git a/test-bitcoincore-rpc/src/state.rs b/test-bitcoincore-rpc/src/state.rs index 1599adbe30..ee64ba6474 100644 --- a/test-bitcoincore-rpc/src/state.rs +++ b/test-bitcoincore-rpc/src/state.rs @@ -2,7 +2,7 @@ use super::*; pub(crate) struct State { pub(crate) blocks: BTreeMap, - pub(crate) descriptors: u64, + pub(crate) descriptors: Vec, pub(crate) fail_lock_unspent: bool, pub(crate) hashes: Vec, pub(crate) locked: BTreeSet, @@ -34,7 +34,7 @@ impl State { Self { blocks, - descriptors: 0, + descriptors: Vec::new(), fail_lock_unspent, hashes, locked: BTreeSet::new(), diff --git a/tests/wallet.rs b/tests/wallet.rs index 7a6f1cc226..5604c9a569 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -1,5 +1,7 @@ use {super::*, std::str::FromStr}; +mod create; + #[test] fn sats() { let rpc_server = test_bitcoincore_rpc::spawn(); @@ -246,7 +248,7 @@ fn inscribe() { .build(); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - assert_eq!(rpc_server.descriptors(), 0); + assert_eq!(rpc_server.descriptors().len(), 0); let stdout = CommandBuilder::new(format!( "--chain regtest wallet inscribe --satpoint {txid}:0:0 hello.txt" @@ -256,7 +258,7 @@ fn inscribe() { .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); - assert_eq!(rpc_server.descriptors(), 1); + assert_eq!(rpc_server.descriptors().len(), 1); rpc_server.mine_blocks(1); @@ -297,7 +299,7 @@ fn inscribe_no_backup() { .build(); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - assert_eq!(rpc_server.descriptors(), 0); + assert_eq!(rpc_server.descriptors().len(), 0); CommandBuilder::new(format!( "--chain regtest wallet inscribe --satpoint {txid}:0:0 hello.txt --no-backup" @@ -307,7 +309,7 @@ fn inscribe_no_backup() { .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); - assert_eq!(rpc_server.descriptors(), 0); + assert_eq!(rpc_server.descriptors().len(), 0); } #[test] @@ -766,21 +768,6 @@ fn inscribe_with_optional_satpoint_arg() { ); } -#[test] -fn create() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); - - assert!(!rpc_server.wallets().contains("ord")); - - CommandBuilder::new("--chain regtest wallet create") - .rpc_server(&rpc_server) - .run(); - - assert!(rpc_server.wallets().contains("ord")); -} - #[test] fn transactions() { let rpc_server = test_bitcoincore_rpc::spawn(); diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs new file mode 100644 index 0000000000..ad1c84c235 --- /dev/null +++ b/tests/wallet/create.rs @@ -0,0 +1,30 @@ +use super::*; + +#[test] +fn create() { + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + + assert!(!rpc_server.wallets().contains("ord")); + + CommandBuilder::new("--chain regtest wallet create") + .rpc_server(&rpc_server) + .run(); + + assert!(rpc_server.wallets().contains("ord")); +} + +#[test] +fn wallet_creates_correct_taproot_descriptor() { + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + + CommandBuilder::new("--chain regtest wallet create") + .rpc_server(&rpc_server) + .run(); + + assert_eq!(rpc_server.descriptors().len(), 1); + assert_regex_match!(&rpc_server.descriptors()[0], "^tr(.*)#.*"); +} From 42c95180392edc7c6e7f3e9eaddd23ef7e37ca34 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 00:56:44 +0100 Subject: [PATCH 02/19] descriptor looks correct now --- src/subcommand/wallet/create.rs | 26 +++++++++++++++++--------- tests/wallet/create.rs | 5 ++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 3320756b8e..c419dff62d 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -1,11 +1,9 @@ use { super::*, bitcoin::secp256k1::rand::RngCore, - bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey}, + bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}, bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, - miniscript::descriptor::{ - Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard, - }, + miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, }; pub(crate) fn run(options: Options) -> Result { @@ -15,17 +13,27 @@ pub(crate) fn run(options: Options) -> Result { .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? .create_wallet("ord", None, Some(true), None, None)?; + let secp256k1 = bitcoin::secp256k1::Secp256k1::new(); + let mut seed = [0; 32]; bitcoin::secp256k1::rand::thread_rng().fill_bytes(&mut seed); + let xkey = ExtendedPrivKey::new_master(options.chain().network(), &seed)?; + let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: None, - xkey: ExtendedPrivKey::new_master(options.chain().network(), &seed)?, - derivation_path: DerivationPath::master(), - wildcard: Wildcard::None, + origin: Some(( + xkey.fingerprint(&secp256k1), + DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { index: 0 }) + .child(ChildNumber::Hardened { index: 0 }), + )), + xkey, + derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 0 }), + wildcard: Wildcard::Unhardened, }); - let public_key = secret_key.to_public(&bitcoin::secp256k1::Secp256k1::new())?; + let public_key = secret_key.to_public(&secp256k1)?; let mut key_map = std::collections::HashMap::new(); key_map.insert(public_key.clone(), secret_key); diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index ad1c84c235..dd1f73b3cb 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -26,5 +26,8 @@ fn wallet_creates_correct_taproot_descriptor() { .run(); assert_eq!(rpc_server.descriptors().len(), 1); - assert_regex_match!(&rpc_server.descriptors()[0], "^tr(.*)#.*"); + assert_regex_match!( + &rpc_server.descriptors()[0], + "tr([.*/86'/0'/0']tprv.*/0/*)#.*" + ); } From 803305435218190e8e4d1b14a5270db10ba06808 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 19:10:11 +0100 Subject: [PATCH 03/19] tests pass --- tests/wallet/create.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index dd1f73b3cb..48ddcf4f3d 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -28,6 +28,6 @@ fn wallet_creates_correct_taproot_descriptor() { assert_eq!(rpc_server.descriptors().len(), 1); assert_regex_match!( &rpc_server.descriptors()[0], - "tr([.*/86'/0'/0']tprv.*/0/*)#.*" + r"tr\(\[.*/86'/0'/0'\]tprv.*/0/\*\)#.*" ); } From 00f9b9dabc2531aed2ed3ab540fa10b97dac72a2 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 19:16:12 +0100 Subject: [PATCH 04/19] tests pass --- src/subcommand/wallet/create.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index c419dff62d..8adf252365 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -14,21 +14,23 @@ pub(crate) fn run(options: Options) -> Result { .create_wallet("ord", None, Some(true), None, None)?; let secp256k1 = bitcoin::secp256k1::Secp256k1::new(); - let mut seed = [0; 32]; bitcoin::secp256k1::rand::thread_rng().fill_bytes(&mut seed); - let xkey = ExtendedPrivKey::new_master(options.chain().network(), &seed)?; + let master_xkey = ExtendedPrivKey::new_master(options.chain().network(), &seed)?; + + let fingerprint = master_xkey.fingerprint(&secp256k1); + + let derivation_path = DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { index: 0 }) + .child(ChildNumber::Hardened { index: 0 }); + + let derived_xkey = master_xkey.derive_priv(&secp256k1, &derivation_path)?; let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some(( - xkey.fingerprint(&secp256k1), - DerivationPath::master() - .child(ChildNumber::Hardened { index: 86 }) - .child(ChildNumber::Hardened { index: 0 }) - .child(ChildNumber::Hardened { index: 0 }), - )), - xkey, + origin: Some((fingerprint, derivation_path)), + xkey: derived_xkey, derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 0 }), wildcard: Wildcard::Unhardened, }); From 6faf863e2a70a42bfb75b3aed86c9650c5548163 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 19:24:56 +0100 Subject: [PATCH 05/19] add change desc --- src/subcommand/wallet/create.rs | 34 +++++++++++++++++++++++++++------ tests/wallet/create.rs | 6 +++++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 8adf252365..91acc9a93a 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -28,24 +28,46 @@ pub(crate) fn run(options: Options) -> Result { let derived_xkey = master_xkey.derive_priv(&secp256k1, &derivation_path)?; - let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some((fingerprint, derivation_path)), + let receive_secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { + origin: Some((fingerprint, derivation_path.clone())), xkey: derived_xkey, derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 0 }), wildcard: Wildcard::Unhardened, }); - let public_key = secret_key.to_public(&secp256k1)?; + let change_secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { + origin: Some((fingerprint, derivation_path)), + xkey: derived_xkey, + derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 1 }), + wildcard: Wildcard::Unhardened, + }); + + let receive_public_key = receive_secret_key.to_public(&secp256k1)?; + let change_public_key = change_secret_key.to_public(&secp256k1)?; let mut key_map = std::collections::HashMap::new(); - key_map.insert(public_key.clone(), secret_key); + key_map.insert(receive_public_key.clone(), receive_secret_key); + key_map.insert(change_public_key.clone(), change_secret_key); + + let receive_desc = Descriptor::new_tr(receive_public_key, None)?; + let change_desc = Descriptor::new_tr(change_public_key, None)?; - let desc = Descriptor::new_tr(public_key, None)?; + options + .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? + .import_descriptors(ImportDescriptors { + descriptor: receive_desc.to_string_with_secret(&key_map), + timestamp: Timestamp::Now, + active: Some(true), + range: None, + next_index: None, + internal: None, + label: None, + })?; options .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? .import_descriptors(ImportDescriptors { - descriptor: desc.to_string_with_secret(&key_map), + descriptor: change_desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, active: Some(true), range: None, diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 48ddcf4f3d..75f2bc4677 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -25,9 +25,13 @@ fn wallet_creates_correct_taproot_descriptor() { .rpc_server(&rpc_server) .run(); - assert_eq!(rpc_server.descriptors().len(), 1); + assert_eq!(rpc_server.descriptors().len(), 2); assert_regex_match!( &rpc_server.descriptors()[0], r"tr\(\[.*/86'/0'/0'\]tprv.*/0/\*\)#.*" ); + assert_regex_match!( + &rpc_server.descriptors()[1], + r"tr\(\[.*/86'/0'/0'\]tprv.*/1/\*\)#.*" + ); } From 8fb4cf4c5c069e257bbfad0a7676496db5c6d0ee Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 22:47:39 +0100 Subject: [PATCH 06/19] add some tests --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/options.rs | 7 +++++++ src/subcommand/wallet/create.rs | 6 +++--- test-bitcoincore-rpc/Cargo.toml | 2 +- test-bitcoincore-rpc/src/api.rs | 5 +++++ test-bitcoincore-rpc/src/lib.rs | 10 +++++++--- test-bitcoincore-rpc/src/server.rs | 28 ++++++++++++++++++++++++---- tests/wallet/create.rs | 19 +++++++++++++++++++ 9 files changed, 69 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d04c60f0f..ddf5b29978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1977,7 +1977,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc" version = "0.16.4" -source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#7028a3030f0ce7ba37f51b2444567924f2460d6d" +source = "git+https://github.com/raphjaph/rust-bitcoincore-rpc?branch=ord-add-list-descriptors#4b68bb87a1fc2c7aada07f33cf0f3dd5fa3712c3" dependencies = [ "jsonrpc", "log", @@ -1989,7 +1989,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc-json" version = "0.16.4" -source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#7028a3030f0ce7ba37f51b2444567924f2460d6d" +source = "git+https://github.com/raphjaph/rust-bitcoincore-rpc?branch=ord-add-list-descriptors#4b68bb87a1fc2c7aada07f33cf0f3dd5fa3712c3" dependencies = [ "bitcoin", "serde", diff --git a/Cargo.toml b/Cargo.toml index d4ab53a312..2c2f076201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ log = "0.4.14" mime = "0.3.16" mime_guess = "2.0.4" miniscript = "9.0.0" -ord-bitcoincore-rpc = { git = "https://github.com/casey/rust-bitcoincore-rpc", branch = "ord" } +ord-bitcoincore-rpc = { git = "https://github.com/raphjaph/rust-bitcoincore-rpc", branch = "ord-add-list-descriptors" } redb = "0.11.0" regex = "1.6.0" rust-embed = "6.4.0" diff --git a/src/options.rs b/src/options.rs index 32c3a1614d..0b62008bd5 100644 --- a/src/options.rs +++ b/src/options.rs @@ -164,6 +164,13 @@ impl Options { ); } } + + for descriptor in client.list_descriptors(None)?.descriptors { + let desc = descriptor.desc; + if !Regex::new(r"(tr|rawtr).*").unwrap().is_match(&desc) { + bail!("the ord wallet should only contain tr and rawtr descriptors: `{desc}`"); + } + } Ok(client) } } diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 91acc9a93a..250d25bd0b 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -10,7 +10,7 @@ pub(crate) fn run(options: Options) -> Result { // TODO: check for correct bitcoin core version options - .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? + .bitcoin_rpc_client_for_wallet_command("ord wallet create")? .create_wallet("ord", None, Some(true), None, None)?; let secp256k1 = bitcoin::secp256k1::Secp256k1::new(); @@ -53,7 +53,7 @@ pub(crate) fn run(options: Options) -> Result { let change_desc = Descriptor::new_tr(change_public_key, None)?; options - .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? + .bitcoin_rpc_client_for_wallet_command("ord wallet create")? .import_descriptors(ImportDescriptors { descriptor: receive_desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, @@ -65,7 +65,7 @@ pub(crate) fn run(options: Options) -> Result { })?; options - .bitcoin_rpc_client_mainnet_forbidden("ord wallet create")? + .bitcoin_rpc_client_for_wallet_command("ord wallet create")? .import_descriptors(ImportDescriptors { descriptor: change_desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, diff --git a/test-bitcoincore-rpc/Cargo.toml b/test-bitcoincore-rpc/Cargo.toml index d318f37c19..bf83bc5e2e 100644 --- a/test-bitcoincore-rpc/Cargo.toml +++ b/test-bitcoincore-rpc/Cargo.toml @@ -13,7 +13,7 @@ hex = "0.4.3" jsonrpc-core = "18.0.0" jsonrpc-derive = "18.0.0" jsonrpc-http-server = "18.0.0" -ord-bitcoincore-rpc = { git = "https://github.com/casey/rust-bitcoincore-rpc", branch = "ord" } +ord-bitcoincore-rpc = { git = "https://github.com/raphjaph/rust-bitcoincore-rpc", branch = "ord-add-list-descriptors" } reqwest = { version = "0.11.10", features = ["blocking"] } serde = { version = "1", features = ["derive"] } serde_json = "1.0.0" diff --git a/test-bitcoincore-rpc/src/api.rs b/test-bitcoincore-rpc/src/api.rs index c329229c87..0a0475af11 100644 --- a/test-bitcoincore-rpc/src/api.rs +++ b/test-bitcoincore-rpc/src/api.rs @@ -139,4 +139,9 @@ pub trait Api { unlock: bool, outputs: Vec, ) -> Result; + + #[rpc(name = "listdescriptors")] + fn list_descriptors( + &self, + ) -> Result; } diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index 6539c2b00b..77cea33825 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -13,12 +13,12 @@ use { Transaction, TxIn, TxMerkleNode, TxOut, Txid, Witness, Wtxid, }, bitcoincore_rpc::json::{ - Bip125Replaceable, CreateRawTransactionInput, EstimateMode, GetBalancesResult, + Bip125Replaceable, CreateRawTransactionInput, Descriptor, EstimateMode, GetBalancesResult, GetBalancesResultEntry, GetBlockHeaderResult, GetBlockchainInfoResult, GetDescriptorInfoResult, GetNetworkInfoResult, GetRawTransactionResult, GetTransactionResult, GetTransactionResultDetail, GetTransactionResultDetailCategory, GetWalletInfoResult, - ListTransactionResult, ListUnspentResultEntry, LoadWalletResult, SignRawTransactionResult, - WalletTxInfo, ImportDescriptors, ImportMultiResult, + ImportDescriptors, ImportMultiResult, ListDescriptorsResult, ListTransactionResult, + ListUnspentResultEntry, LoadWalletResult, SignRawTransactionResult, Timestamp, WalletTxInfo, }, jsonrpc_core::{IoHandler, Value}, jsonrpc_http_server::{CloseHandle, ServerBuilder}, @@ -215,6 +215,10 @@ impl Handle { self.state().descriptors.clone() } + pub fn import_descriptor(&self, desc: String) { + self.state().descriptors.push(desc); + } + pub fn sent(&self) -> Vec { self.state().sent.clone() } diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 06fd2e4f38..03e054bc95 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -440,10 +440,11 @@ impl Api for Server { &self, req: Vec, ) -> Result, jsonrpc_core::Error> { - self - .state() - .descriptors - .extend(req.into_iter().map(|params| String::from(params.descriptor))); + self.state().descriptors.extend( + req + .into_iter() + .map(|params| String::from(params.descriptor)), + ); Ok(vec![ImportMultiResult { success: true, @@ -533,4 +534,23 @@ impl Api for Server { Ok(true) } + + fn list_descriptors(&self) -> Result { + Ok(ListDescriptorsResult { + wallet_name: "ord".into(), + descriptors: self + .state() + .descriptors + .iter() + .map(|desc| Descriptor { + desc: desc.to_string(), + timestamp: Timestamp::Now, + active: true, + internal: None, + range: None, + next: None, + }) + .collect(), + }) + } } diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 75f2bc4677..828b4c7146 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -35,3 +35,22 @@ fn wallet_creates_correct_taproot_descriptor() { r"tr\(\[.*/86'/0'/0'\]tprv.*/1/\*\)#.*" ); } + +#[test] +fn detect_wrong_descriptors() { + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + + CommandBuilder::new("--chain regtest wallet create") + .rpc_server(&rpc_server) + .run(); + + rpc_server.import_descriptor("wpkh([aslfjk])#a23ad2l".to_string()); + + CommandBuilder::new("--chain regtest wallet create") + .rpc_server(&rpc_server) + .stderr_regex("error: the ord wallet should only contain tr and rawtr descriptors: .*") + .expected_exit_code(1) + .run(); +} From bf54d10812126c8388c162cac0e3dab5056f345c Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 22:52:28 +0100 Subject: [PATCH 07/19] fmt --- src/subcommand/wallet/inscribe.rs | 2 +- test-bitcoincore-rpc/src/api.rs | 4 +--- test-bitcoincore-rpc/src/server.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index c9b2b12b45..475f6d90eb 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -267,7 +267,7 @@ impl Inscribe { range: None, next_index: None, internal: Some(false), - label: Some(format!("commit tx recovery key")), + label: Some("commit tx recovery key".to_string()), })?; for result in response { diff --git a/test-bitcoincore-rpc/src/api.rs b/test-bitcoincore-rpc/src/api.rs index 0a0475af11..cfaa635acb 100644 --- a/test-bitcoincore-rpc/src/api.rs +++ b/test-bitcoincore-rpc/src/api.rs @@ -141,7 +141,5 @@ pub trait Api { ) -> Result; #[rpc(name = "listdescriptors")] - fn list_descriptors( - &self, - ) -> Result; + fn list_descriptors(&self) -> Result; } diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 03e054bc95..3e38ab0e32 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -443,7 +443,7 @@ impl Api for Server { self.state().descriptors.extend( req .into_iter() - .map(|params| String::from(params.descriptor)), + .map(|params| params.descriptor), ); Ok(vec![ImportMultiResult { From 061c99a03cb67744bb5face0dc131770d4bc6751 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 4 Jan 2023 22:54:08 +0100 Subject: [PATCH 08/19] use caseys crate --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- test-bitcoincore-rpc/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddf5b29978..d33a37beab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1977,7 +1977,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc" version = "0.16.4" -source = "git+https://github.com/raphjaph/rust-bitcoincore-rpc?branch=ord-add-list-descriptors#4b68bb87a1fc2c7aada07f33cf0f3dd5fa3712c3" +source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#45fef7914dbf6d0caa4e73610a4aca52e2f92e20" dependencies = [ "jsonrpc", "log", @@ -1989,7 +1989,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc-json" version = "0.16.4" -source = "git+https://github.com/raphjaph/rust-bitcoincore-rpc?branch=ord-add-list-descriptors#4b68bb87a1fc2c7aada07f33cf0f3dd5fa3712c3" +source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#45fef7914dbf6d0caa4e73610a4aca52e2f92e20" dependencies = [ "bitcoin", "serde", diff --git a/Cargo.toml b/Cargo.toml index 2c2f076201..d4ab53a312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ log = "0.4.14" mime = "0.3.16" mime_guess = "2.0.4" miniscript = "9.0.0" -ord-bitcoincore-rpc = { git = "https://github.com/raphjaph/rust-bitcoincore-rpc", branch = "ord-add-list-descriptors" } +ord-bitcoincore-rpc = { git = "https://github.com/casey/rust-bitcoincore-rpc", branch = "ord" } redb = "0.11.0" regex = "1.6.0" rust-embed = "6.4.0" diff --git a/test-bitcoincore-rpc/Cargo.toml b/test-bitcoincore-rpc/Cargo.toml index bf83bc5e2e..d318f37c19 100644 --- a/test-bitcoincore-rpc/Cargo.toml +++ b/test-bitcoincore-rpc/Cargo.toml @@ -13,7 +13,7 @@ hex = "0.4.3" jsonrpc-core = "18.0.0" jsonrpc-derive = "18.0.0" jsonrpc-http-server = "18.0.0" -ord-bitcoincore-rpc = { git = "https://github.com/raphjaph/rust-bitcoincore-rpc", branch = "ord-add-list-descriptors" } +ord-bitcoincore-rpc = { git = "https://github.com/casey/rust-bitcoincore-rpc", branch = "ord" } reqwest = { version = "0.11.10", features = ["blocking"] } serde = { version = "1", features = ["derive"] } serde_json = "1.0.0" From 0f4acbca723e658b238d7c91ac864ef6f24f1342 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 5 Jan 2023 00:23:02 +0100 Subject: [PATCH 09/19] check version --- src/options.rs | 39 ++++++++++++++++++++++----- src/subcommand/preview.rs | 4 +-- src/subcommand/wallet.rs | 4 +-- src/subcommand/wallet/balance.rs | 2 +- src/subcommand/wallet/create.rs | 8 +++--- src/subcommand/wallet/inscribe.rs | 22 +-------------- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/subcommand/wallet/transactions.rs | 2 +- test-bitcoincore-rpc/src/server.rs | 9 +++---- 10 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/options.rs b/src/options.rs index 0b62008bd5..5999dd12df 100644 --- a/src/options.rs +++ b/src/options.rs @@ -107,7 +107,16 @@ impl Options { Ok(self.chain().join_with_data_dir(&base)) } - pub(crate) fn bitcoin_rpc_client(&self) -> Result { + fn format_bitcoin_core_version(version: usize) -> String { + format!( + "{}.{}.{}", + version / 10000, + version % 10000 / 100, + version % 100 + ) + } + + pub(crate) fn bitcoin_rpc_client(&self, min_version: usize) -> Result { let cookie_file = self.cookie_file()?; let rpc_url = self.rpc_url(); log::info!( @@ -132,11 +141,24 @@ impl Options { bail!("Bitcoin RPC server is on {rpc_chain} but ord is on {ord_chain}"); } + let bitcoin_version = client.version()?; + if bitcoin_version < min_version { + bail!( + "Bitcoin Core {} or newer required, current version is {}", + Self::format_bitcoin_core_version(min_version), + Self::format_bitcoin_core_version(bitcoin_version), + ); + } + Ok(client) } - pub(crate) fn bitcoin_rpc_client_mainnet_forbidden(&self, command: &str) -> Result { - let client = self.bitcoin_rpc_client()?; + pub(crate) fn bitcoin_rpc_client_mainnet_forbidden( + &self, + command: &str, + min_version: usize, + ) -> Result { + let client = self.bitcoin_rpc_client(min_version)?; if self.chain() == Chain::Mainnet { bail!("`{command}` is unstable and not yet supported on mainnet."); @@ -144,8 +166,12 @@ impl Options { Ok(client) } - pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, command: &str) -> Result { - let client = self.bitcoin_rpc_client()?; + pub(crate) fn bitcoin_rpc_client_for_wallet_command( + &self, + command: &str, + min_version: usize, + ) -> Result { + let client = self.bitcoin_rpc_client(min_version)?; if self.chain() == Chain::Mainnet { let wallet_info = client.get_wallet_info()?; @@ -171,6 +197,7 @@ impl Options { bail!("the ord wallet should only contain tr and rawtr descriptors: `{desc}`"); } } + Ok(client) } } @@ -418,7 +445,7 @@ mod tests { .unwrap(); assert_eq!( - options.bitcoin_rpc_client().unwrap_err().to_string(), + options.bitcoin_rpc_client(0).unwrap_err().to_string(), "Bitcoin RPC server is on testnet but ord is on mainnet" ); } diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index c35d5a9272..39f89f0f64 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -39,7 +39,7 @@ impl Preview { }; for attempt in 0.. { - if options.bitcoin_rpc_client().is_ok() { + if options.bitcoin_rpc_client(0).is_ok() { break; } @@ -50,7 +50,7 @@ impl Preview { thread::sleep(Duration::from_millis(50)); } - let rpc_client = options.bitcoin_rpc_client()?; + let rpc_client = options.bitcoin_rpc_client(0)?; super::wallet::create::run(options.clone())?; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 58c0acd094..8d1b99e8d0 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -64,7 +64,7 @@ fn get_unspent_output_ranges( } fn get_unspent_outputs(options: &Options) -> Result> { - let client = options.bitcoin_rpc_client()?; + let client = options.bitcoin_rpc_client(0)?; let mut utxos = BTreeMap::new(); @@ -97,7 +97,7 @@ fn get_unspent_outputs(options: &Options) -> Result> } fn get_change_addresses(options: &Options, n: usize) -> Result> { - let client = options.bitcoin_rpc_client()?; + let client = options.bitcoin_rpc_client(0)?; let mut addresses = Vec::new(); for _ in 0..n { diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index f375fc8981..b7f7de9467 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -4,7 +4,7 @@ pub(crate) fn run(options: Options) -> Result { println!( "{}", options - .bitcoin_rpc_client()? + .bitcoin_rpc_client(0)? .get_balances()? .mine .trusted diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 250d25bd0b..e186aba3f6 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -7,10 +7,8 @@ use { }; pub(crate) fn run(options: Options) -> Result { - // TODO: check for correct bitcoin core version - options - .bitcoin_rpc_client_for_wallet_command("ord wallet create")? + .bitcoin_rpc_client_for_wallet_command("ord wallet create", 240000)? .create_wallet("ord", None, Some(true), None, None)?; let secp256k1 = bitcoin::secp256k1::Secp256k1::new(); @@ -53,7 +51,7 @@ pub(crate) fn run(options: Options) -> Result { let change_desc = Descriptor::new_tr(change_public_key, None)?; options - .bitcoin_rpc_client_for_wallet_command("ord wallet create")? + .bitcoin_rpc_client_for_wallet_command("ord wallet create", 240000)? .import_descriptors(ImportDescriptors { descriptor: receive_desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, @@ -65,7 +63,7 @@ pub(crate) fn run(options: Options) -> Result { })?; options - .bitcoin_rpc_client_for_wallet_command("ord wallet create")? + .bitcoin_rpc_client_for_wallet_command("ord wallet create", 240000)? .import_descriptors(ImportDescriptors { descriptor: change_desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 475f6d90eb..7e18e771fc 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -16,17 +16,6 @@ use { std::collections::BTreeSet, }; -const MIN_BITCOIN_VERSION: usize = 240000; - -fn format_bitcoin_core_version(version: usize) -> String { - format!( - "{}.{}.{}", - version / 10000, - version % 10000 / 100, - version % 100 - ) -} - #[derive(Debug, Parser)] pub(crate) struct Inscribe { #[clap(long, help = "Inscribe ")] @@ -39,16 +28,7 @@ pub(crate) struct Inscribe { impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { - let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe")?; - - let bitcoin_version = client.version()?; - if bitcoin_version < MIN_BITCOIN_VERSION { - bail!( - "Bitcoin Core {} or newer required, current version is {}", - format_bitcoin_core_version(MIN_BITCOIN_VERSION), - format_bitcoin_core_version(bitcoin_version), - ); - } + let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe", 240000)?; let inscription = Inscription::from_file(options.chain(), &self.file)?; diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 97c572adc1..b5f10bb93a 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -4,7 +4,7 @@ pub(crate) fn run(options: Options) -> Result { println!( "{}", options - .bitcoin_rpc_client_for_wallet_command("ord wallet receive")? + .bitcoin_rpc_client_for_wallet_command("ord wallet receive", 0)? .get_new_address(None, None)? ); diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index b914f71823..7f15c4dbca 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -8,7 +8,7 @@ pub(crate) struct Send { impl Send { pub(crate) fn run(self, options: Options) -> Result { - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet send")?; + let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet send", 0)?; if !self.address.is_valid_for_network(options.chain().network()) { bail!( diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 729406a8b7..3bc12509eb 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -8,7 +8,7 @@ pub(crate) struct Transactions { impl Transactions { pub(crate) fn run(self, options: Options) -> Result { - for tx in options.bitcoin_rpc_client()?.list_transactions( + for tx in options.bitcoin_rpc_client(0)?.list_transactions( None, Some(self.limit.unwrap_or(u16::MAX).into()), None, diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 3e38ab0e32..98c462cc3f 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -440,11 +440,10 @@ impl Api for Server { &self, req: Vec, ) -> Result, jsonrpc_core::Error> { - self.state().descriptors.extend( - req - .into_iter() - .map(|params| params.descriptor), - ); + self + .state() + .descriptors + .extend(req.into_iter().map(|params| params.descriptor)); Ok(vec![ImportMultiResult { success: true, From 935d56872e6573d69bf9379c8c23901ad18725b3 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 5 Jan 2023 19:23:17 +0100 Subject: [PATCH 10/19] check number of descriptors --- src/options.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/options.rs b/src/options.rs index 5999dd12df..0243a4f163 100644 --- a/src/options.rs +++ b/src/options.rs @@ -191,12 +191,21 @@ impl Options { } } + let mut _num_rawtr = 0; + let mut num_tr = 0; for descriptor in client.list_descriptors(None)?.descriptors { let desc = descriptor.desc; - if !Regex::new(r"(tr|rawtr).*").unwrap().is_match(&desc) { + if Regex::new(r"tr.*").unwrap().is_match(&desc) { + num_tr += 1; + } else if Regex::new(r"rawtr.*").unwrap().is_match(&desc) { + num_rawtr += 1; + } else { bail!("the ord wallet should only contain tr and rawtr descriptors: `{desc}`"); } } + if num_tr > 2 || num_tr == 0 { + bail!("the ord wallet should contain 2 tr descriptors"); + } Ok(client) } From f029770b655d29f771ef6eeda31ace8291bf9f46 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 5 Jan 2023 19:26:28 +0100 Subject: [PATCH 11/19] tweak --- src/options.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/options.rs b/src/options.rs index 0243a4f163..44f69c59db 100644 --- a/src/options.rs +++ b/src/options.rs @@ -191,19 +191,17 @@ impl Options { } } - let mut _num_rawtr = 0; let mut num_tr = 0; for descriptor in client.list_descriptors(None)?.descriptors { let desc = descriptor.desc; if Regex::new(r"tr.*").unwrap().is_match(&desc) { num_tr += 1; } else if Regex::new(r"rawtr.*").unwrap().is_match(&desc) { - num_rawtr += 1; } else { bail!("the ord wallet should only contain tr and rawtr descriptors: `{desc}`"); } } - if num_tr > 2 || num_tr == 0 { + if num_tr > 2 { bail!("the ord wallet should contain 2 tr descriptors"); } From d145826ef021b2d2e65bff4afdbaba609f682905 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 6 Jan 2023 01:29:38 +0100 Subject: [PATCH 12/19] finally done --- src/options.rs | 71 ++++++++-------- src/subcommand/preview.rs | 9 +- src/subcommand/wallet.rs | 8 +- src/subcommand/wallet/balance.rs | 2 +- src/subcommand/wallet/create.rs | 118 ++++++++++++++++---------- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/subcommand/wallet/transactions.rs | 15 ++-- test-bitcoincore-rpc/src/lib.rs | 9 ++ tests/lib.rs | 6 ++ tests/server.rs | 9 ++ tests/wallet.rs | 94 ++++++++++++++------ tests/wallet/create.rs | 39 +++++++-- 14 files changed, 258 insertions(+), 128 deletions(-) diff --git a/src/options.rs b/src/options.rs index 44f69c59db..5e21928631 100644 --- a/src/options.rs +++ b/src/options.rs @@ -116,7 +116,7 @@ impl Options { ) } - pub(crate) fn bitcoin_rpc_client(&self, min_version: usize) -> Result { + pub(crate) fn bitcoin_rpc_client(&self) -> Result { let cookie_file = self.cookie_file()?; let rpc_url = self.rpc_url(); log::info!( @@ -141,37 +141,32 @@ impl Options { bail!("Bitcoin RPC server is on {rpc_chain} but ord is on {ord_chain}"); } - let bitcoin_version = client.version()?; - if bitcoin_version < min_version { - bail!( - "Bitcoin Core {} or newer required, current version is {}", - Self::format_bitcoin_core_version(min_version), - Self::format_bitcoin_core_version(bitcoin_version), - ); - } - Ok(client) } - pub(crate) fn bitcoin_rpc_client_mainnet_forbidden( - &self, - command: &str, - min_version: usize, - ) -> Result { - let client = self.bitcoin_rpc_client(min_version)?; - + pub(crate) fn bitcoin_rpc_client_mainnet_forbidden(&self, command: &str) -> Result { if self.chain() == Chain::Mainnet { bail!("`{command}` is unstable and not yet supported on mainnet."); } + + let client = self.bitcoin_rpc_client_for_wallet_command(command)?; + Ok(client) } - pub(crate) fn bitcoin_rpc_client_for_wallet_command( - &self, - command: &str, - min_version: usize, - ) -> Result { - let client = self.bitcoin_rpc_client(min_version)?; + pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, command: &str) -> Result { + let client = self.bitcoin_rpc_client()?; + + const MIN_VERSION: usize = 240000; + + let bitcoin_version = client.version()?; + if bitcoin_version < MIN_VERSION { + bail!( + "Bitcoin Core {} or newer required, current version is {}", + Self::format_bitcoin_core_version(MIN_VERSION), + Self::format_bitcoin_core_version(bitcoin_version), + ); + } if self.chain() == Chain::Mainnet { let wallet_info = client.get_wallet_info()?; @@ -191,19 +186,25 @@ impl Options { } } - let mut num_tr = 0; - for descriptor in client.list_descriptors(None)?.descriptors { - let desc = descriptor.desc; - if Regex::new(r"tr.*").unwrap().is_match(&desc) { - num_tr += 1; - } else if Regex::new(r"rawtr.*").unwrap().is_match(&desc) { - } else { - bail!("the ord wallet should only contain tr and rawtr descriptors: `{desc}`"); + if !command.contains("create") { + let descriptors = client.list_descriptors(None)?.descriptors; + + let tr = descriptors + .iter() + .filter(|descriptor| descriptor.desc.starts_with("tr(")) + .count(); + + let rawtr = descriptors + .iter() + .filter(|descriptor| descriptor.desc.starts_with("rawtr(")) + .count(); + + if tr != 2 || descriptors.len() != 2 + rawtr { + bail!( + "this does not appear to be an ord wallet, please create one using `ord wallet create`" + ); } } - if num_tr > 2 { - bail!("the ord wallet should contain 2 tr descriptors"); - } Ok(client) } @@ -452,7 +453,7 @@ mod tests { .unwrap(); assert_eq!( - options.bitcoin_rpc_client(0).unwrap_err().to_string(), + options.bitcoin_rpc_client().unwrap_err().to_string(), "Bitcoin RPC server is on testnet but ord is on mainnet" ); } diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 39f89f0f64..c5bee6a9b9 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -39,7 +39,7 @@ impl Preview { }; for attempt in 0.. { - if options.bitcoin_rpc_client(0).is_ok() { + if options.bitcoin_rpc_client().is_ok() { break; } @@ -50,9 +50,12 @@ impl Preview { thread::sleep(Duration::from_millis(50)); } - let rpc_client = options.bitcoin_rpc_client(0)?; + let rpc_client = options.bitcoin_rpc_client()?; - super::wallet::create::run(options.clone())?; + super::wallet::create::Create::run( + &super::wallet::create::Create { name: "ord".into() }, + options.clone(), + )?; let address = rpc_client.get_new_address(None, None)?; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 8d1b99e8d0..34489b1bb2 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -16,7 +16,7 @@ pub(crate) enum Wallet { #[clap(about = "Get wallet balance")] Balance, #[clap(about = "Create a new wallet")] - Create, + Create(create::Create), #[clap(about = "Create an inscription")] Inscribe(inscribe::Inscribe), #[clap(about = "List wallet inscriptions")] @@ -37,7 +37,7 @@ impl Wallet { pub(crate) fn run(self, options: Options) -> Result { match self { Self::Balance => balance::run(options), - Self::Create => create::run(options), + Self::Create(create) => create.run(options), Self::Inscribe(inscribe) => inscribe.run(options), Self::Inscriptions => inscriptions::run(options), Self::Receive => receive::run(options), @@ -64,7 +64,7 @@ fn get_unspent_output_ranges( } fn get_unspent_outputs(options: &Options) -> Result> { - let client = options.bitcoin_rpc_client(0)?; + let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet")?; let mut utxos = BTreeMap::new(); @@ -97,7 +97,7 @@ fn get_unspent_outputs(options: &Options) -> Result> } fn get_change_addresses(options: &Options, n: usize) -> Result> { - let client = options.bitcoin_rpc_client(0)?; + let client = options.bitcoin_rpc_client()?; let mut addresses = Vec::new(); for _ in 0..n { diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index b7f7de9467..5860b77c6e 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -4,7 +4,7 @@ pub(crate) fn run(options: Options) -> Result { println!( "{}", options - .bitcoin_rpc_client(0)? + .bitcoin_rpc_client_for_wallet_command("ord wallet balance")? .get_balances()? .mine .trusted diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index e186aba3f6..7b56a6a385 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -1,71 +1,97 @@ use { super::*, - bitcoin::secp256k1::rand::RngCore, - bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}, + bitcoin::secp256k1::{rand::RngCore, All, Secp256k1}, + bitcoin::{ + util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}, + Network, + }, bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, }; -pub(crate) fn run(options: Options) -> Result { - options - .bitcoin_rpc_client_for_wallet_command("ord wallet create", 240000)? - .create_wallet("ord", None, Some(true), None, None)?; +#[derive(Debug, Parser)] +pub(crate) struct Create { + #[clap(long, default_value = "ord", help = "Give to wallet")] + pub(crate) name: String, +} - let secp256k1 = bitcoin::secp256k1::Secp256k1::new(); - let mut seed = [0; 32]; - bitcoin::secp256k1::rand::thread_rng().fill_bytes(&mut seed); +impl Create { + pub(crate) fn run(&self, options: Options) -> Result { + if !(self.name == "ord" || self.name.starts_with("ord-")) { + bail!("`ord wallet create` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); + } - let master_xkey = ExtendedPrivKey::new_master(options.chain().network(), &seed)?; + options + .bitcoin_rpc_client_for_wallet_command("ord wallet create")? + .create_wallet(&self.name, None, Some(true), None, None)?; - let fingerprint = master_xkey.fingerprint(&secp256k1); + let secp = bitcoin::secp256k1::Secp256k1::new(); + let mut seed = [0; 32]; + bitcoin::secp256k1::rand::thread_rng().fill_bytes(&mut seed); - let derivation_path = DerivationPath::master() - .child(ChildNumber::Hardened { index: 86 }) - .child(ChildNumber::Hardened { index: 0 }) - .child(ChildNumber::Hardened { index: 0 }); + let master_private_key = ExtendedPrivKey::new_master(options.chain().network(), &seed)?; - let derived_xkey = master_xkey.derive_priv(&secp256k1, &derivation_path)?; + let fingerprint = master_private_key.fingerprint(&secp); - let receive_secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some((fingerprint, derivation_path.clone())), - xkey: derived_xkey, - derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 0 }), - wildcard: Wildcard::Unhardened, - }); + let derivation_path = if options.chain().network() == Network::Bitcoin { + DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { index: 0 }) + .child(ChildNumber::Hardened { index: 0 }) + } else { + DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { index: 1 }) + .child(ChildNumber::Hardened { index: 0 }) + }; + + let derived_private_key = master_private_key.derive_priv(&secp, &derivation_path)?; - let change_secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some((fingerprint, derivation_path)), - xkey: derived_xkey, - derivation_path: DerivationPath::master().child(ChildNumber::Normal { index: 1 }), + derive_and_import_descriptor( + &options, + &secp, + (fingerprint, derivation_path.clone()), + derived_private_key, + DerivationPath::master().child(ChildNumber::Normal { index: 0 }), + )?; + + derive_and_import_descriptor( + &options, + &secp, + (fingerprint, derivation_path), + derived_private_key, + DerivationPath::master().child(ChildNumber::Normal { index: 1 }), + )?; + + Ok(()) + } +} + +fn derive_and_import_descriptor( + options: &Options, + secp: &Secp256k1, + origin: (Fingerprint, DerivationPath), + derived_private_key: ExtendedPrivKey, + derivation_path: DerivationPath, +) -> Result { + let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { + origin: Some(origin), + xkey: derived_private_key, + derivation_path, wildcard: Wildcard::Unhardened, }); - let receive_public_key = receive_secret_key.to_public(&secp256k1)?; - let change_public_key = change_secret_key.to_public(&secp256k1)?; + let public_key = secret_key.to_public(secp)?; let mut key_map = std::collections::HashMap::new(); - key_map.insert(receive_public_key.clone(), receive_secret_key); - key_map.insert(change_public_key.clone(), change_secret_key); - - let receive_desc = Descriptor::new_tr(receive_public_key, None)?; - let change_desc = Descriptor::new_tr(change_public_key, None)?; + key_map.insert(public_key.clone(), secret_key); - options - .bitcoin_rpc_client_for_wallet_command("ord wallet create", 240000)? - .import_descriptors(ImportDescriptors { - descriptor: receive_desc.to_string_with_secret(&key_map), - timestamp: Timestamp::Now, - active: Some(true), - range: None, - next_index: None, - internal: None, - label: None, - })?; + let desc = Descriptor::new_tr(public_key, None)?; options - .bitcoin_rpc_client_for_wallet_command("ord wallet create", 240000)? + .bitcoin_rpc_client_for_wallet_command("ord wallet create")? .import_descriptors(ImportDescriptors { - descriptor: change_desc.to_string_with_secret(&key_map), + descriptor: desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, active: Some(true), range: None, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 7e18e771fc..c51e1d247b 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -28,7 +28,7 @@ pub(crate) struct Inscribe { impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { - let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe", 240000)?; + let client = options.bitcoin_rpc_client_mainnet_forbidden("ord wallet inscribe")?; let inscription = Inscription::from_file(options.chain(), &self.file)?; diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index b5f10bb93a..97c572adc1 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -4,7 +4,7 @@ pub(crate) fn run(options: Options) -> Result { println!( "{}", options - .bitcoin_rpc_client_for_wallet_command("ord wallet receive", 0)? + .bitcoin_rpc_client_for_wallet_command("ord wallet receive")? .get_new_address(None, None)? ); diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index efc41d9d99..c00e1d2522 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -8,7 +8,7 @@ pub(crate) struct Send { impl Send { pub(crate) fn run(self, options: Options) -> Result { - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet send", 0)?; + let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet send")?; if !self.address.is_valid_for_network(options.chain().network()) { bail!( diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 3bc12509eb..6d330e049e 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -8,12 +8,15 @@ pub(crate) struct Transactions { impl Transactions { pub(crate) fn run(self, options: Options) -> Result { - for tx in options.bitcoin_rpc_client(0)?.list_transactions( - None, - Some(self.limit.unwrap_or(u16::MAX).into()), - None, - None, - )? { + for tx in options + .bitcoin_rpc_client_for_wallet_command("ord wallet transactions")? + .list_transactions( + None, + Some(self.limit.unwrap_or(u16::MAX).into()), + None, + None, + )? + { println!("{}\t{}", tx.info.txid, tx.info.confirmations); } diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index 73d35c35c5..dbc9df19dc 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -227,6 +227,15 @@ impl Handle { pub fn lock(&self, output: OutPoint) { self.state().locked.insert(output); } + + pub fn network_flag(&self) -> String { + match self.state().network { + Network::Bitcoin => "".into(), + Network::Testnet => "--chain testnet".into(), + Network::Signet => "--chain signet".into(), + Network::Regtest => "--chain regtest".into(), + } + } } impl Drop for Handle { diff --git a/tests/lib.rs b/tests/lib.rs index cc264e8cda..a5878d8dcd 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -64,6 +64,12 @@ fn create_inscription(rpc_server: &test_bitcoincore_rpc::Handle, filename: &str) inscription_id } +fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { + CommandBuilder::new(format!("{} wallet create", rpc_server.network_flag())) + .rpc_server(rpc_server) + .run(); +} + mod command_builder; mod epochs; mod expected; diff --git a/tests/server.rs b/tests/server.rs index 2fcc63e308..d91287055b 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -39,6 +39,7 @@ fn inscription_page() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -80,6 +81,7 @@ fn inscription_appears_on_reveal_transaction_page() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -105,6 +107,7 @@ fn inscription_page_after_send() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -154,6 +157,8 @@ fn inscription_content() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); + let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -188,6 +193,7 @@ fn home_page_includes_latest_inscriptions() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let inscription_id = create_inscription(&rpc_server, "foo.png"); @@ -207,6 +213,7 @@ fn home_page_inscriptions_are_sorted() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let mut inscriptions = String::new(); @@ -230,6 +237,7 @@ fn inscriptions_page() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -262,6 +270,7 @@ fn inscriptions_page_is_sorted() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let mut inscriptions = String::new(); diff --git a/tests/wallet.rs b/tests/wallet.rs index a258eb1c45..4c32d6ebf6 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -4,10 +4,13 @@ mod create; #[test] fn sats() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + create_wallet(&rpc_server); let second_coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - CommandBuilder::new("--index-sats wallet sats") + CommandBuilder::new("--chain regtest --index-sats wallet sats") .rpc_server(&rpc_server) .expected_stdout(format!( "{}\t{}\t0\tuncommon\n", @@ -19,10 +22,13 @@ fn sats() { #[test] fn sats_from_tsv_success() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + create_wallet(&rpc_server); let second_coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") + CommandBuilder::new("--chain regtest --index-sats wallet sats --tsv foo.tsv") .write("foo.tsv", "nvtcsezkbtg") .rpc_server(&rpc_server) .expected_stdout(format!( @@ -34,8 +40,12 @@ fn sats_from_tsv_success() { #[test] fn sats_from_tsv_parse_error() { - let rpc_server = test_bitcoincore_rpc::spawn(); - CommandBuilder::new("wallet sats --tsv foo.tsv") + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + create_wallet(&rpc_server); + + CommandBuilder::new("--chain regtest wallet sats --tsv foo.tsv") .write("foo.tsv", "===") .rpc_server(&rpc_server) .expected_exit_code(1) @@ -47,8 +57,11 @@ fn sats_from_tsv_parse_error() { #[test] fn sats_from_tsv_file_not_found() { - let rpc_server = test_bitcoincore_rpc::spawn(); - CommandBuilder::new("wallet sats --tsv foo.tsv") + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + create_wallet(&rpc_server); + CommandBuilder::new("--chain regtest wallet sats --tsv foo.tsv") .rpc_server(&rpc_server) .expected_exit_code(1) .stderr_regex("error: I/O error reading `.*`\nbecause: .*\n") @@ -60,6 +73,7 @@ fn send_works_on_signet() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -112,6 +126,7 @@ fn send_unknown_inscription() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -129,6 +144,7 @@ fn send_inscribed_sat() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -165,14 +181,13 @@ fn send_inscribed_sat() { #[test] fn send_on_mainnnet_refuses_to_work_with_wallet_name_foo() { - let rpc_server = test_bitcoincore_rpc::builder().wallet_name("foo").build(); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let rpc_server = test_bitcoincore_rpc::builder().build(); CommandBuilder::new( - format!("wallet send bc1qzjeg3h996kw24zrg69nge97fw8jc4v7v7yznftzk06j3429t52vse9tkp9 {txid}:0:0"), + "wallet create --name foo".to_string(), ) .rpc_server(&rpc_server) - .expected_stderr("error: `ord wallet send` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") + .expected_stderr("error: `ord wallet create` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") .expected_exit_code(1) .run(); } @@ -180,7 +195,8 @@ fn send_on_mainnnet_refuses_to_work_with_wallet_name_foo() { #[test] fn send_addresses_must_be_valid_for_network() { let rpc_server = test_bitcoincore_rpc::builder().build(); - let txid = rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0].txid(); + let txid = rpc_server.mine_blocks_with_subsidy(1, 1_000)[0].txdata[0].txid(); + create_wallet(&rpc_server); CommandBuilder::new(format!( "wallet send tb1qx4gf3ya0cxfcwydpq8vr2lhrysneuj5d7lqatw {txid}:0:0" @@ -197,6 +213,7 @@ fn send_addresses_must_be_valid_for_network() { fn send_on_mainnnet_works_with_wallet_named_ord() { let rpc_server = test_bitcoincore_rpc::builder().build(); let txid = rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0].txid(); + create_wallet(&rpc_server); let stdout = CommandBuilder::new(format!( "wallet send bc1qzjeg3h996kw24zrg69nge97fw8jc4v7v7yznftzk06j3429t52vse9tkp9 {txid}:0:0" @@ -214,6 +231,7 @@ fn send_on_mainnnet_works_with_wallet_whose_name_starts_with_ord() { let rpc_server = test_bitcoincore_rpc::builder() .wallet_name("ord-foo") .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -267,7 +285,8 @@ fn inscribe_no_backup() { .build(); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet(&rpc_server); + assert_eq!(rpc_server.descriptors().len(), 2); CommandBuilder::new(format!( "--chain regtest wallet inscribe --satpoint {txid}:0:0 hello.txt --no-backup" @@ -277,7 +296,7 @@ fn inscribe_no_backup() { .stdout_regex("commit\t[[:xdigit:]]{64}\nreveal\t[[:xdigit:]]{64}\n") .run(); - assert_eq!(rpc_server.descriptors().len(), 0); + assert_eq!(rpc_server.descriptors().len(), 2); } #[test] @@ -301,6 +320,8 @@ fn inscribe() { assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet(&rpc_server); + let stdout = CommandBuilder::new(format!( "--chain regtest wallet inscribe --satpoint {txid}:0:0 hello.txt" )) @@ -311,7 +332,7 @@ fn inscribe() { let inscription_id = reveal_txid_from_inscribe_stdout(&stdout); - assert_eq!(rpc_server.descriptors().len(), 1); + assert_eq!(rpc_server.descriptors().len(), 3); rpc_server.mine_blocks(1); @@ -331,6 +352,7 @@ fn inscribe_unknown_file_extension() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( @@ -348,6 +370,7 @@ fn inscribe_exceeds_push_byte_limit() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( @@ -367,6 +390,7 @@ fn regtest_has_no_content_size_limit() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( @@ -383,6 +407,7 @@ fn inscribe_does_not_use_inscribed_sats_as_cardinal_utxos() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks_with_subsidy(1, 800)[0].txdata[0].txid(); CommandBuilder::new(format!( "--chain regtest wallet inscribe --satpoint {txid}:0:0 degenerate.png" @@ -409,6 +434,7 @@ fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks_with_subsidy(1, 800)[0].txdata[0].txid(); CommandBuilder::new(format!( "--chain regtest wallet inscribe --satpoint {txid}:0:0 degenerate.png" @@ -434,6 +460,7 @@ fn refuse_to_reinscribe_sats() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks_with_subsidy(1, 800)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -465,6 +492,7 @@ fn do_not_accidentally_send_an_inscription() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -500,6 +528,7 @@ fn refuse_to_inscribe_already_inscribed_utxo() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -536,6 +565,7 @@ fn inscriptions_cannot_be_sent_by_satpoint() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let stdout = CommandBuilder::new(format!( @@ -563,6 +593,7 @@ fn inscriptions_cannot_be_sent_by_satpoint() { #[test] fn receive() { let rpc_server = test_bitcoincore_rpc::spawn(); + create_wallet(&rpc_server); let stdout = CommandBuilder::new("wallet receive") .rpc_server(&rpc_server) @@ -575,6 +606,7 @@ fn receive() { #[test] fn outputs() { let rpc_server = test_bitcoincore_rpc::spawn(); + create_wallet(&rpc_server); let coinbase_tx = &rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -589,6 +621,7 @@ fn outputs() { #[test] fn outputs_includes_locked_outputs() { let rpc_server = test_bitcoincore_rpc::spawn(); + create_wallet(&rpc_server); let coinbase_tx = &rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -608,6 +641,7 @@ fn inscriptions() { .network(Network::Signet) .wallet_name("ord-wallet") .build(); + create_wallet(&rpc_server); let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); let inscription_id = reveal_txid_from_inscribe_stdout( @@ -660,6 +694,7 @@ fn inscriptions_includes_locked_utxos() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); + create_wallet(&rpc_server); rpc_server.mine_blocks(1); @@ -689,6 +724,7 @@ fn inscribe_with_optional_satpoint_arg() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); rpc_server.mine_blocks(1); let stdout = CommandBuilder::new("--chain regtest wallet inscribe hello.txt") @@ -712,15 +748,18 @@ fn inscribe_with_optional_satpoint_arg() { #[test] fn transactions() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + create_wallet(&rpc_server); - CommandBuilder::new("wallet transactions") + CommandBuilder::new("--chain regtest wallet transactions") .rpc_server(&rpc_server) .run(); rpc_server.mine_blocks(1); - CommandBuilder::new("wallet transactions") + CommandBuilder::new("--chain regtest wallet transactions") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n") .run(); @@ -728,27 +767,30 @@ fn transactions() { #[test] fn transactions_with_limit() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); + create_wallet(&rpc_server); - CommandBuilder::new("wallet transactions") + CommandBuilder::new("--chain regtest wallet transactions") .rpc_server(&rpc_server) .run(); rpc_server.mine_blocks(1); - CommandBuilder::new("wallet transactions") + CommandBuilder::new("--chain regtest wallet transactions") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n") .run(); rpc_server.mine_blocks(1); - CommandBuilder::new("wallet transactions") + CommandBuilder::new("--chain regtest wallet transactions") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n[[:xdigit:]]{64}\t2\n") .run(); - CommandBuilder::new("wallet transactions --limit 1") + CommandBuilder::new("--chain regtest wallet transactions --limit 1") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n") .run(); @@ -759,6 +801,7 @@ fn wallet_balance() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); CommandBuilder::new("--regtest wallet balance") .rpc_server(&rpc_server) @@ -778,6 +821,7 @@ fn send_btc() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); rpc_server.mine_blocks(1); @@ -805,6 +849,7 @@ fn send_btc_locks_inscriptions() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + create_wallet(&rpc_server); rpc_server.mine_blocks(1); @@ -846,6 +891,7 @@ fn send_btc_fails_if_lock_unspent_fails() { .fail_lock_unspent(true) .network(Network::Regtest) .build(); + create_wallet(&rpc_server); rpc_server.mine_blocks(1); diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 828b4c7146..80bf0c893f 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -15,24 +15,45 @@ fn create() { assert!(rpc_server.wallets().contains("ord")); } +#[test] +fn wallet_creates_correct_mainnet_taproot_descriptor() { + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Bitcoin) + .build(); + + CommandBuilder::new("wallet create") + .rpc_server(&rpc_server) + .run(); + + assert_eq!(rpc_server.descriptors().len(), 2); + assert_regex_match!( + &rpc_server.descriptors()[0], + r"tr\(\[[[:xdigit:]]{8}/86'/0'/0'\]xprv[[:alnum:]]*/0/\*\)#[[:alnum:]]{8}" + ); + assert_regex_match!( + &rpc_server.descriptors()[1], + r"tr\(\[[[:xdigit:]]{8}/86'/0'/0'\]xprv[[:alnum:]]*/1/\*\)#[[:alnum:]]{8}" + ); +} + #[test] fn wallet_creates_correct_taproot_descriptor() { let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) + .network(Network::Signet) .build(); - CommandBuilder::new("--chain regtest wallet create") + CommandBuilder::new("--chain signet wallet create") .rpc_server(&rpc_server) .run(); assert_eq!(rpc_server.descriptors().len(), 2); assert_regex_match!( &rpc_server.descriptors()[0], - r"tr\(\[.*/86'/0'/0'\]tprv.*/0/\*\)#.*" + r"tr\(\[[[:xdigit:]]{8}/86'/1'/0'\]tprv[[:alnum:]]*/0/\*\)#[[:alnum:]]{8}" ); assert_regex_match!( &rpc_server.descriptors()[1], - r"tr\(\[.*/86'/0'/0'\]tprv.*/1/\*\)#.*" + r"tr\(\[[[:xdigit:]]{8}/86'/1'/0'\]tprv[[:alnum:]]*/1/\*\)#[[:alnum:]]{8}" ); } @@ -48,9 +69,15 @@ fn detect_wrong_descriptors() { rpc_server.import_descriptor("wpkh([aslfjk])#a23ad2l".to_string()); - CommandBuilder::new("--chain regtest wallet create") + CommandBuilder::new("--chain regtest wallet transactions") .rpc_server(&rpc_server) - .stderr_regex("error: the ord wallet should only contain tr and rawtr descriptors: .*") + .stderr_regex( + "error: this does not appear to be an ord wallet, please create one using `ord wallet create`\n", + ) .expected_exit_code(1) .run(); } + +#[ignore] +#[test] +fn consecutive_create_throws_error() {} From 814a978f656ad1c68d9e054333160b4f81be6ca8 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 6 Jan 2023 01:44:30 +0100 Subject: [PATCH 13/19] tweak --- src/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.rs b/src/options.rs index 5e21928631..d2dfa7ff2c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -186,7 +186,7 @@ impl Options { } } - if !command.contains("create") { + if command != "ord wallet create" { let descriptors = client.list_descriptors(None)?.descriptors; let tr = descriptors From 25a0f50d1dd1e9d17adb4a7c99ef57c4927fd888 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 6 Jan 2023 17:11:33 +0100 Subject: [PATCH 14/19] probably not done yet but have a look --- src/options.rs | 23 ++++++------- src/subcommand/wallet.rs | 2 +- src/subcommand/wallet/create.rs | 53 +++++++++++++----------------- test-bitcoincore-rpc/src/lib.rs | 10 +++--- test-bitcoincore-rpc/src/server.rs | 15 ++++++--- tests/lib.rs | 2 +- tests/wallet.rs | 7 ++-- tests/wallet/create.rs | 19 +++++++++-- 8 files changed, 74 insertions(+), 57 deletions(-) diff --git a/src/options.rs b/src/options.rs index d2dfa7ff2c..237d4f1ba8 100644 --- a/src/options.rs +++ b/src/options.rs @@ -168,25 +168,26 @@ impl Options { ); } - if self.chain() == Chain::Mainnet { - let wallet_info = client.get_wallet_info()?; + if command != "ord wallet create" { + if self.chain() == Chain::Mainnet { + let wallet_info = client.get_wallet_info()?; - if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { - bail!("`{command}` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); - } + if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { + bail!("`{command}` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); + } - let balances = client.get_balances()?; + let balances = client.get_balances()?; - let total = balances.mine.trusted + balances.mine.untrusted_pending + balances.mine.immature; + let total = + balances.mine.trusted + balances.mine.untrusted_pending + balances.mine.immature; - if total > Amount::from_sat(1_000_000) { - bail!( + if total > Amount::from_sat(1_000_000) { + bail!( "`{command}` may not be used on mainnet with wallets containing more than 1,000,000 sats" ); + } } - } - if command != "ord wallet create" { let descriptors = client.list_descriptors(None)?.descriptors; let tr = descriptors diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 3c85dddcb0..6f0e274a3a 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -97,7 +97,7 @@ fn get_unspent_outputs(options: &Options) -> Result> } fn get_change_addresses(options: &Options, n: usize) -> Result> { - let client = options.bitcoin_rpc_client()?; + let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet")?; let mut addresses = Vec::new(); for _ in 0..n { diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 7b56a6a385..8191d33221 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -11,19 +11,19 @@ use { #[derive(Debug, Parser)] pub(crate) struct Create { - #[clap(long, default_value = "ord", help = "Give to wallet")] + #[clap(long, default_value = "ord", help = "Create wallet with ")] pub(crate) name: String, } impl Create { pub(crate) fn run(&self, options: Options) -> Result { if !(self.name == "ord" || self.name.starts_with("ord-")) { - bail!("`ord wallet create` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); + bail!("`ord wallet create` may only be used with a wallet named `ord` or whose name starts with `ord-`"); } - options - .bitcoin_rpc_client_for_wallet_command("ord wallet create")? - .create_wallet(&self.name, None, Some(true), None, None)?; + let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet create")?; + + client.create_wallet(&self.name, None, Some(true), None, None)?; let secp = bitcoin::secp256k1::Secp256k1::new(); let mut seed = [0; 32]; @@ -33,22 +33,17 @@ impl Create { let fingerprint = master_private_key.fingerprint(&secp); - let derivation_path = if options.chain().network() == Network::Bitcoin { - DerivationPath::master() - .child(ChildNumber::Hardened { index: 86 }) - .child(ChildNumber::Hardened { index: 0 }) - .child(ChildNumber::Hardened { index: 0 }) - } else { - DerivationPath::master() - .child(ChildNumber::Hardened { index: 86 }) - .child(ChildNumber::Hardened { index: 1 }) - .child(ChildNumber::Hardened { index: 0 }) - }; + let derivation_path = DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { + index: u32::from(options.chain().network() != Network::Bitcoin), + }) + .child(ChildNumber::Hardened { index: 0 }); let derived_private_key = master_private_key.derive_priv(&secp, &derivation_path)?; derive_and_import_descriptor( - &options, + &client, &secp, (fingerprint, derivation_path.clone()), derived_private_key, @@ -56,7 +51,7 @@ impl Create { )?; derive_and_import_descriptor( - &options, + &client, &secp, (fingerprint, derivation_path), derived_private_key, @@ -68,7 +63,7 @@ impl Create { } fn derive_and_import_descriptor( - options: &Options, + client: &Client, secp: &Secp256k1, origin: (Fingerprint, DerivationPath), derived_private_key: ExtendedPrivKey, @@ -88,17 +83,15 @@ fn derive_and_import_descriptor( let desc = Descriptor::new_tr(public_key, None)?; - options - .bitcoin_rpc_client_for_wallet_command("ord wallet create")? - .import_descriptors(ImportDescriptors { - descriptor: desc.to_string_with_secret(&key_map), - timestamp: Timestamp::Now, - active: Some(true), - range: None, - next_index: None, - internal: None, - label: None, - })?; + client.import_descriptors(ImportDescriptors { + descriptor: desc.to_string_with_secret(&key_map), + timestamp: Timestamp::Now, + active: Some(true), + range: None, + next_index: None, + internal: None, + label: None, + })?; Ok(()) } diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index dbc9df19dc..b70b2ae98e 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -228,12 +228,12 @@ impl Handle { self.state().locked.insert(output); } - pub fn network_flag(&self) -> String { + pub fn network(&self) -> String { match self.state().network { - Network::Bitcoin => "".into(), - Network::Testnet => "--chain testnet".into(), - Network::Signet => "--chain signet".into(), - Network::Regtest => "--chain regtest".into(), + Network::Bitcoin => "mainnet".to_string(), + Network::Testnet => Network::Testnet.to_string(), + Network::Signet => Network::Signet.to_string(), + Network::Regtest => Network::Regtest.to_string(), } } } diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 98c462cc3f..c790b26465 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -225,10 +225,17 @@ impl Api for Server { _passphrase: Option, _avoid_reuse: Option, ) -> Result { - self.state().wallets.insert(name.clone()); - Ok(LoadWalletResult { - name, - warning: None, + if self.state().wallets.insert(name.clone()) { + return Ok(LoadWalletResult { + name, + warning: None, + }); + } + + Err(jsonrpc_core::Error { + code: jsonrpc_core::ErrorCode::ServerError(-4), + message: "wallet already exists".to_string(), + data: None, }) } diff --git a/tests/lib.rs b/tests/lib.rs index a5878d8dcd..63839b5f31 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -65,7 +65,7 @@ fn create_inscription(rpc_server: &test_bitcoincore_rpc::Handle, filename: &str) } fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { - CommandBuilder::new(format!("{} wallet create", rpc_server.network_flag())) + CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) .rpc_server(rpc_server) .run(); } diff --git a/tests/wallet.rs b/tests/wallet.rs index a55c6bbcd2..4ac3964d12 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -182,13 +182,14 @@ fn send_inscribed_sat() { #[test] fn send_on_mainnnet_refuses_to_work_with_wallet_name_foo() { - let rpc_server = test_bitcoincore_rpc::builder().build(); + let rpc_server = test_bitcoincore_rpc::builder().wallet_name("foo").build(); + let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new( - "wallet create --name foo".to_string(), + format!("wallet send bc1qzjeg3h996kw24zrg69nge97fw8jc4v7v7yznftzk06j3429t52vse9tkp9 {txid}:0:0") ) .rpc_server(&rpc_server) - .expected_stderr("error: `ord wallet create` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") + .expected_stderr("error: `ord wallet send` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") .expected_exit_code(1) .run(); } diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 80bf0c893f..a0d98a5900 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -78,6 +78,21 @@ fn detect_wrong_descriptors() { .run(); } -#[ignore] #[test] -fn consecutive_create_throws_error() {} +fn consecutive_create_throws_error() { + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Bitcoin) + .build(); + + CommandBuilder::new("wallet create") + .rpc_server(&rpc_server) + .run(); + + CommandBuilder::new("wallet create") + .rpc_server(&rpc_server) + .expected_exit_code(1) + .expected_stderr( + "error: JSON-RPC error: RPC error response: RpcError { code: -4, message: \"wallet already exists\", data: None }\n" + ) + .run(); +} From 7da571027bea3581f67d25a03ce3d25b6481df5a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 9 Jan 2023 19:40:36 +0100 Subject: [PATCH 15/19] remove balance check --- src/options.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/options.rs b/src/options.rs index a4dc6f06bf..d40bb9cefa 100644 --- a/src/options.rs +++ b/src/options.rs @@ -164,25 +164,6 @@ impl Options { } if command != "ord wallet create" { - if self.chain() == Chain::Mainnet { - let wallet_info = client.get_wallet_info()?; - - if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { - bail!("`{command}` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); - } - - let balances = client.get_balances()?; - - let total = - balances.mine.trusted + balances.mine.untrusted_pending + balances.mine.immature; - - if total > Amount::from_sat(1_000_000) { - bail!( - "`{command}` may not be used on mainnet with wallets containing more than 1,000,000 sats" - ); - } - } - let descriptors = client.list_descriptors(None)?.descriptors; let tr = descriptors From 31d3260e749474c0a60d772bbabdc22d864d36a9 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 9 Jan 2023 19:45:40 +0100 Subject: [PATCH 16/19] make flag --- src/options.rs | 7 ++++--- src/subcommand/wallet/create.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/options.rs b/src/options.rs index d40bb9cefa..567ceea11f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -144,14 +144,15 @@ impl Options { Ok(client) } - pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, command: &str) -> Result { + pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, create: bool) -> Result { let client = self.bitcoin_rpc_client()?; let wallet_info = client.get_wallet_info()?; if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { - bail!("`{command}` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); + bail!("wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); } + const MIN_VERSION: usize = 240000; let bitcoin_version = client.version()?; @@ -163,7 +164,7 @@ impl Options { ); } - if command != "ord wallet create" { + if !create { let descriptors = client.list_descriptors(None)?.descriptors; let tr = descriptors diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 8191d33221..cee73efea5 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -21,7 +21,7 @@ impl Create { bail!("`ord wallet create` may only be used with a wallet named `ord` or whose name starts with `ord-`"); } - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet create")?; + let client = options.bitcoin_rpc_client_for_wallet_command(true)?; client.create_wallet(&self.name, None, Some(true), None, None)?; From cf52f896b8bcb469bf22e6a13237c6b7522e070b Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 9 Jan 2023 19:50:39 +0100 Subject: [PATCH 17/19] fix tests --- src/options.rs | 9 +++++---- src/subcommand/wallet.rs | 4 ++-- src/subcommand/wallet/balance.rs | 2 +- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/subcommand/wallet/transactions.rs | 2 +- tests/wallet.rs | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/options.rs b/src/options.rs index 567ceea11f..dfa23cf7d9 100644 --- a/src/options.rs +++ b/src/options.rs @@ -149,10 +149,6 @@ impl Options { let wallet_info = client.get_wallet_info()?; - if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { - bail!("wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); - } - const MIN_VERSION: usize = 240000; let bitcoin_version = client.version()?; @@ -165,6 +161,11 @@ impl Options { } if !create { + + if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { + bail!("wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); + } + let descriptors = client.list_descriptors(None)?.descriptors; let tr = descriptors diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 6f0e274a3a..934db626cf 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -64,7 +64,7 @@ fn get_unspent_output_ranges( } fn get_unspent_outputs(options: &Options) -> Result> { - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet")?; + let client = options.bitcoin_rpc_client_for_wallet_command(false)?; let mut utxos = BTreeMap::new(); @@ -97,7 +97,7 @@ fn get_unspent_outputs(options: &Options) -> Result> } fn get_change_addresses(options: &Options, n: usize) -> Result> { - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet")?; + let client = options.bitcoin_rpc_client_for_wallet_command(false)?; let mut addresses = Vec::new(); for _ in 0..n { diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index 5860b77c6e..42189cb599 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -4,7 +4,7 @@ pub(crate) fn run(options: Options) -> Result { println!( "{}", options - .bitcoin_rpc_client_for_wallet_command("ord wallet balance")? + .bitcoin_rpc_client_for_wallet_command(false)? .get_balances()? .mine .trusted diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 200afe3f20..e83460fa15 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -28,7 +28,7 @@ pub(crate) struct Inscribe { impl Inscribe { pub(crate) fn run(self, options: Options) -> Result { - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet inscribe")?; + let client = options.bitcoin_rpc_client_for_wallet_command(false)?; let inscription = Inscription::from_file(options.chain(), &self.file)?; diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 5501fbcba6..277f30098c 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -9,7 +9,7 @@ pub(crate) struct Receive { impl Receive { pub(crate) fn run(self, options: Options) -> Result { let address = options - .bitcoin_rpc_client_for_wallet_command("ord wallet receive")? + .bitcoin_rpc_client_for_wallet_command(false)? .get_new_address(None, None)?; if self.cardinal { diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 9dd32a0d4b..2934270da3 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -10,7 +10,7 @@ pub(crate) struct Send { impl Send { pub(crate) fn run(self, options: Options) -> Result { - let client = options.bitcoin_rpc_client_for_wallet_command("ord wallet send")?; + let client = options.bitcoin_rpc_client_for_wallet_command(false)?; if !self.cardinal && !self.address.is_ordinal() { bail!("refusing to send to cardinal adddress, which may be from wallet without sat control; the `--cardinal` flag bypasses this check"); diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 6d330e049e..c9e027b799 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -9,7 +9,7 @@ pub(crate) struct Transactions { impl Transactions { pub(crate) fn run(self, options: Options) -> Result { for tx in options - .bitcoin_rpc_client_for_wallet_command("ord wallet transactions")? + .bitcoin_rpc_client_for_wallet_command(false)? .list_transactions( None, Some(self.limit.unwrap_or(u16::MAX).into()), diff --git a/tests/wallet.rs b/tests/wallet.rs index e16d01d581..1667c237a6 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -189,7 +189,7 @@ fn send_on_mainnnet_refuses_to_work_with_wallet_name_foo() { format!("wallet send bc1qzjeg3h996kw24zrg69nge97fw8jc4v7v7yznftzk06j3429t52vse9tkp9 {txid}:0:0") ) .rpc_server(&rpc_server) - .expected_stderr("error: `ord wallet send` may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") + .expected_stderr("error: wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") .expected_exit_code(1) .run(); } From abb730c18d51c75c437a2d9c5174ad022f9a5b53 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 9 Jan 2023 20:24:55 +0100 Subject: [PATCH 18/19] done --- src/options.rs | 5 +--- test-bitcoincore-rpc/src/server.rs | 15 +++------- tests/wallet.rs | 46 +++++++++++------------------- tests/wallet/create.rs | 38 +++++++++--------------- 4 files changed, 35 insertions(+), 69 deletions(-) diff --git a/src/options.rs b/src/options.rs index dfa23cf7d9..a35e081446 100644 --- a/src/options.rs +++ b/src/options.rs @@ -161,7 +161,6 @@ impl Options { } if !create { - if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { bail!("wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); } @@ -179,9 +178,7 @@ impl Options { .count(); if tr != 2 || descriptors.len() != 2 + rawtr { - bail!( - "this does not appear to be an ord wallet, please create one using `ord wallet create`" - ); + bail!("this does not appear to be an ord wallet, create one with `ord wallet create`"); } } diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index c790b26465..98c462cc3f 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -225,17 +225,10 @@ impl Api for Server { _passphrase: Option, _avoid_reuse: Option, ) -> Result { - if self.state().wallets.insert(name.clone()) { - return Ok(LoadWalletResult { - name, - warning: None, - }); - } - - Err(jsonrpc_core::Error { - code: jsonrpc_core::ErrorCode::ServerError(-4), - message: "wallet already exists".to_string(), - data: None, + self.state().wallets.insert(name.clone()); + Ok(LoadWalletResult { + name, + warning: None, }) } diff --git a/tests/wallet.rs b/tests/wallet.rs index 1667c237a6..ec845afd01 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -4,13 +4,11 @@ mod create; #[test] fn sats() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); let second_coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - CommandBuilder::new("--chain regtest --index-sats wallet sats") + CommandBuilder::new("--index-sats wallet sats") .rpc_server(&rpc_server) .expected_stdout(format!( "{}\t{}\t0\tuncommon\n", @@ -22,13 +20,11 @@ fn sats() { #[test] fn sats_from_tsv_success() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); let second_coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); - CommandBuilder::new("--chain regtest --index-sats wallet sats --tsv foo.tsv") + CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") .write("foo.tsv", "nvtcsezkbtg") .rpc_server(&rpc_server) .expected_stdout(format!( @@ -40,12 +36,10 @@ fn sats_from_tsv_success() { #[test] fn sats_from_tsv_parse_error() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - CommandBuilder::new("--chain regtest wallet sats --tsv foo.tsv") + CommandBuilder::new("wallet sats --tsv foo.tsv") .write("foo.tsv", "===") .rpc_server(&rpc_server) .expected_exit_code(1) @@ -57,11 +51,9 @@ fn sats_from_tsv_parse_error() { #[test] fn sats_from_tsv_file_not_found() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - CommandBuilder::new("--chain regtest wallet sats --tsv foo.tsv") + CommandBuilder::new("wallet sats --tsv foo.tsv") .rpc_server(&rpc_server) .expected_exit_code(1) .stderr_regex("error: I/O error reading `.*`\nbecause: .*\n") @@ -186,7 +178,7 @@ fn send_on_mainnnet_refuses_to_work_with_wallet_name_foo() { let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new( - format!("wallet send bc1qzjeg3h996kw24zrg69nge97fw8jc4v7v7yznftzk06j3429t52vse9tkp9 {txid}:0:0") + format!("wallet send ord1qcqgs2pps4u4yedfyl5pysdjjncs8et5u8gcumw {txid}:0:0") ) .rpc_server(&rpc_server) .expected_stderr("error: wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`\n") @@ -737,18 +729,16 @@ fn inscribe_with_optional_satpoint_arg() { #[test] fn transactions() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - CommandBuilder::new("--chain regtest wallet transactions") + CommandBuilder::new("wallet transactions") .rpc_server(&rpc_server) .run(); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet transactions") + CommandBuilder::new("wallet transactions") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n") .run(); @@ -756,30 +746,28 @@ fn transactions() { #[test] fn transactions_with_limit() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); create_wallet(&rpc_server); - CommandBuilder::new("--chain regtest wallet transactions") + CommandBuilder::new("wallet transactions") .rpc_server(&rpc_server) .run(); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet transactions") + CommandBuilder::new("wallet transactions") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n") .run(); rpc_server.mine_blocks(1); - CommandBuilder::new("--chain regtest wallet transactions") + CommandBuilder::new("wallet transactions") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n[[:xdigit:]]{64}\t2\n") .run(); - CommandBuilder::new("--chain regtest wallet transactions --limit 1") + CommandBuilder::new("wallet transactions --limit 1") .rpc_server(&rpc_server) .stdout_regex("[[:xdigit:]]{64}\t1\n") .run(); diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index a0d98a5900..896eea9ebe 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -2,13 +2,11 @@ use super::*; #[test] fn create() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); assert!(!rpc_server.wallets().contains("ord")); - CommandBuilder::new("--chain regtest wallet create") + CommandBuilder::new("wallet create") .rpc_server(&rpc_server) .run(); @@ -17,9 +15,7 @@ fn create() { #[test] fn wallet_creates_correct_mainnet_taproot_descriptor() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Bitcoin) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("wallet create") .rpc_server(&rpc_server) @@ -37,7 +33,7 @@ fn wallet_creates_correct_mainnet_taproot_descriptor() { } #[test] -fn wallet_creates_correct_taproot_descriptor() { +fn wallet_creates_correct_test_network_taproot_descriptor() { let rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); @@ -59,40 +55,32 @@ fn wallet_creates_correct_taproot_descriptor() { #[test] fn detect_wrong_descriptors() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Regtest) - .build(); + let rpc_server = test_bitcoincore_rpc::spawn(); - CommandBuilder::new("--chain regtest wallet create") + CommandBuilder::new("wallet create") .rpc_server(&rpc_server) .run(); rpc_server.import_descriptor("wpkh([aslfjk])#a23ad2l".to_string()); - CommandBuilder::new("--chain regtest wallet transactions") + CommandBuilder::new("wallet transactions") .rpc_server(&rpc_server) .stderr_regex( - "error: this does not appear to be an ord wallet, please create one using `ord wallet create`\n", + "error: this does not appear to be an ord wallet, create one with `ord wallet create`\n", ) .expected_exit_code(1) .run(); } #[test] -fn consecutive_create_throws_error() { - let rpc_server = test_bitcoincore_rpc::builder() - .network(Network::Bitcoin) - .build(); - - CommandBuilder::new("wallet create") - .rpc_server(&rpc_server) - .run(); +fn create_wallet_with_wrong_name() { + let rpc_server = test_bitcoincore_rpc::spawn(); - CommandBuilder::new("wallet create") + CommandBuilder::new("wallet create --name nft-wallet") .rpc_server(&rpc_server) - .expected_exit_code(1) .expected_stderr( - "error: JSON-RPC error: RPC error response: RpcError { code: -4, message: \"wallet already exists\", data: None }\n" + "error: `ord wallet create` may only be used with a wallet named `ord` or whose name starts with `ord-`\n", ) + .expected_exit_code(1) .run(); } From 8d4aad3cf5c09c04a68ed7953f37fbeb9fc73363 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 9 Jan 2023 21:11:26 +0100 Subject: [PATCH 19/19] add bech32m support --- Cargo.lock | 4 ++-- src/options.rs | 4 ++-- src/subcommand/wallet.rs | 2 +- src/subcommand/wallet/create.rs | 12 +++++++----- src/subcommand/wallet/receive.rs | 2 +- test-bitcoincore-rpc/src/api.rs | 7 +++++-- test-bitcoincore-rpc/src/server.rs | 7 +++++-- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39ade32dfa..3238b553c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1978,7 +1978,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc" version = "0.16.4" -source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#45fef7914dbf6d0caa4e73610a4aca52e2f92e20" +source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#1bc2638f5a3049495a7aa953c4c6dac44fd1524b" dependencies = [ "jsonrpc", "log", @@ -1990,7 +1990,7 @@ dependencies = [ [[package]] name = "ord-bitcoincore-rpc-json" version = "0.16.4" -source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#45fef7914dbf6d0caa4e73610a4aca52e2f92e20" +source = "git+https://github.com/casey/rust-bitcoincore-rpc?branch=ord#1bc2638f5a3049495a7aa953c4c6dac44fd1524b" dependencies = [ "bitcoin", "serde", diff --git a/src/options.rs b/src/options.rs index a35e081446..4abc4b9276 100644 --- a/src/options.rs +++ b/src/options.rs @@ -147,8 +147,6 @@ impl Options { pub(crate) fn bitcoin_rpc_client_for_wallet_command(&self, create: bool) -> Result { let client = self.bitcoin_rpc_client()?; - let wallet_info = client.get_wallet_info()?; - const MIN_VERSION: usize = 240000; let bitcoin_version = client.version()?; @@ -161,6 +159,8 @@ impl Options { } if !create { + let wallet_info = client.get_wallet_info()?; + if !(wallet_info.wallet_name == "ord" || wallet_info.wallet_name.starts_with("ord-")) { bail!("wallet commands may only be used on mainnet with a wallet named `ord` or whose name starts with `ord-`"); } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 934db626cf..82f28c5b9c 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -103,7 +103,7 @@ fn get_change_addresses(options: &Options, n: usize) -> Result> { for _ in 0..n { addresses.push( client - .call("getrawchangeaddress", &[]) + .call("getrawchangeaddress", &["bech32m".into()]) .context("could not get change addresses from wallet")?, ); } diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index cee73efea5..e697d0085b 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -47,7 +47,7 @@ impl Create { &secp, (fingerprint, derivation_path.clone()), derived_private_key, - DerivationPath::master().child(ChildNumber::Normal { index: 0 }), + false, )?; derive_and_import_descriptor( @@ -55,7 +55,7 @@ impl Create { &secp, (fingerprint, derivation_path), derived_private_key, - DerivationPath::master().child(ChildNumber::Normal { index: 1 }), + true, )?; Ok(()) @@ -67,12 +67,14 @@ fn derive_and_import_descriptor( secp: &Secp256k1, origin: (Fingerprint, DerivationPath), derived_private_key: ExtendedPrivKey, - derivation_path: DerivationPath, + change: bool, ) -> Result { let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { origin: Some(origin), xkey: derived_private_key, - derivation_path, + derivation_path: DerivationPath::master().child(ChildNumber::Normal { + index: change.into(), + }), wildcard: Wildcard::Unhardened, }); @@ -89,7 +91,7 @@ fn derive_and_import_descriptor( active: Some(true), range: None, next_index: None, - internal: None, + internal: Some(!change), label: None, })?; diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 277f30098c..4a9e3f10c1 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -10,7 +10,7 @@ impl Receive { pub(crate) fn run(self, options: Options) -> Result { let address = options .bitcoin_rpc_client_for_wallet_command(false)? - .get_new_address(None, None)?; + .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; if self.cardinal { println!("{}", address); diff --git a/test-bitcoincore-rpc/src/api.rs b/test-bitcoincore-rpc/src/api.rs index cfaa635acb..cc95b62bea 100644 --- a/test-bitcoincore-rpc/src/api.rs +++ b/test-bitcoincore-rpc/src/api.rs @@ -103,7 +103,10 @@ pub trait Api { fn list_lock_unspent(&self) -> Result, jsonrpc_core::Error>; #[rpc(name = "getrawchangeaddress")] - fn get_raw_change_address(&self) -> Result; + fn get_raw_change_address( + &self, + address_type: Option, + ) -> Result; #[rpc(name = "getdescriptorinfo")] fn get_descriptor_info( @@ -121,7 +124,7 @@ pub trait Api { fn get_new_address( &self, label: Option, - address_type: Option<()>, + address_type: Option, ) -> Result; #[rpc(name = "listtransactions")] diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 98c462cc3f..22f51d495a 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -414,7 +414,10 @@ impl Api for Server { ) } - fn get_raw_change_address(&self) -> Result { + fn get_raw_change_address( + &self, + _address_type: Option, + ) -> Result { let secp256k1 = Secp256k1::new(); let key_pair = KeyPair::new(&secp256k1, &mut rand::thread_rng()); let (public_key, _parity) = XOnlyPublicKey::from_keypair(&key_pair); @@ -455,7 +458,7 @@ impl Api for Server { fn get_new_address( &self, _label: Option, - _address_type: Option<()>, + _address_type: Option, ) -> Result { let secp256k1 = Secp256k1::new(); let key_pair = KeyPair::new(&secp256k1, &mut rand::thread_rng());