From a9f9e70b739a238471ee062906e7cb268e711be5 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 8 Feb 2023 00:26:33 +0100 Subject: [PATCH] Ensure wallet commands load wallet (#1524) --- src/index.rs | 39 ++++++++++++++++++++++++--- src/lib.rs | 1 + src/subcommand/wallet.rs | 14 +--------- src/subcommand/wallet/balance.rs | 5 ++-- src/subcommand/wallet/inscribe.rs | 3 ++- src/subcommand/wallet/inscriptions.rs | 4 +-- src/subcommand/wallet/outputs.rs | 4 +-- src/subcommand/wallet/sats.rs | 4 +-- src/subcommand/wallet/send.rs | 4 +-- src/wallet.rs | 13 +++++++++ test-bitcoincore-rpc/src/lib.rs | 10 ------- test-bitcoincore-rpc/src/server.rs | 38 ++++++++++++++------------ test-bitcoincore-rpc/src/state.rs | 9 +------ tests/wallet/send.rs | 19 ------------- 14 files changed, 84 insertions(+), 83 deletions(-) create mode 100644 src/wallet.rs diff --git a/src/index.rs b/src/index.rs index 1484e09f68..8a04f9ab14 100644 --- a/src/index.rs +++ b/src/index.rs @@ -7,6 +7,7 @@ use { updater::Updater, }, super::*, + crate::wallet::Wallet, bitcoin::BlockHeader, bitcoincore_rpc::{json::GetBlockHeaderResult, Auth, Client}, chrono::SubsecRound, @@ -242,7 +243,7 @@ impl Index { }) } - pub(crate) fn get_unspent_outputs(&self) -> Result> { + pub(crate) fn get_unspent_outputs(&self, _wallet: Wallet) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( self @@ -285,6 +286,21 @@ impl Index { Ok(utxos) } + pub(crate) fn get_unspent_output_ranges( + &self, + wallet: Wallet, + ) -> Result)>> { + self + .get_unspent_outputs(wallet)? + .into_keys() + .map(|outpoint| match self.list(outpoint)? { + Some(List::Unspent(sat_ranges)) => Ok((outpoint, sat_ranges)), + Some(List::Spent) => bail!("output {outpoint} in wallet but is spent according to index"), + None => bail!("index has not seen {outpoint}"), + }) + .collect() + } + pub(crate) fn has_sat_index(&self) -> Result { match self.begin_read()?.0.open_table(OUTPOINT_TO_SAT_RANGES) { Ok(_) => Ok(true), @@ -887,7 +903,10 @@ impl Index { #[cfg(test)] mod tests { - use super::*; + use { + super::*, + bitcoin::secp256k1::rand::{self, RngCore}, + }; struct ContextBuilder { args: Vec, @@ -900,7 +919,9 @@ mod tests { } fn try_build(self) -> Result { - let rpc_server = test_bitcoincore_rpc::spawn(); + let rpc_server = test_bitcoincore_rpc::builder() + .network(Network::Regtest) + .build(); let tempdir = self.tempdir.unwrap_or_else(|| TempDir::new().unwrap()); let cookie_file = tempdir.path().join("cookie"); @@ -922,6 +943,7 @@ mod tests { index.update().unwrap(); Ok(Context { + options, rpc_server, tempdir, index, @@ -945,6 +967,7 @@ mod tests { } struct Context { + options: Options, rpc_server: test_bitcoincore_rpc::Handle, #[allow(unused)] tempdir: TempDir, @@ -2099,9 +2122,17 @@ mod tests { #[test] fn unsynced_index_fails() { for context in Context::configurations() { + let mut entropy = [0; 16]; + rand::thread_rng().fill_bytes(&mut entropy); + let mnemonic = Mnemonic::from_entropy(&entropy).unwrap(); + crate::subcommand::wallet::initialize_wallet(&context.options, mnemonic.to_seed("")).unwrap(); context.rpc_server.mine_blocks(1); assert_regex_match!( - context.index.get_unspent_outputs().unwrap_err().to_string(), + context + .index + .get_unspent_outputs(Wallet::load(&context.options).unwrap()) + .unwrap_err() + .to_string(), r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" ); } diff --git a/src/lib.rs b/src/lib.rs index 134a861a9e..a753711385 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,6 +119,7 @@ mod sat_point; pub mod subcommand; mod tally; mod templates; +mod wallet; type Result = std::result::Result; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 1cf6e95692..18a46ef04c 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -67,25 +67,13 @@ impl Wallet { } } -fn get_unspent_output_ranges(index: &Index) -> Result)>> { - index - .get_unspent_outputs()? - .into_keys() - .map(|outpoint| match index.list(outpoint)? { - Some(List::Unspent(sat_ranges)) => Ok((outpoint, sat_ranges)), - Some(List::Spent) => bail!("output {outpoint} in wallet but is spent according to index"), - None => bail!("index has not seen {outpoint}"), - }) - .collect() -} - fn get_change_address(client: &Client) -> Result
{ client .call("getrawchangeaddress", &["bech32m".into()]) .context("could not get change addresses from wallet") } -fn initialize_wallet(options: &Options, seed: [u8; 64]) -> Result { +pub(crate) fn initialize_wallet(options: &Options, seed: [u8; 64]) -> Result { let client = options.bitcoin_rpc_client_for_wallet_command(true)?; let network = options.chain().network(); diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index 4487f691a5..84ff68d2b9 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -1,5 +1,4 @@ -use super::*; -use std::collections::BTreeSet; +use {super::*, crate::wallet::Wallet, std::collections::BTreeSet}; #[derive(Serialize, Deserialize)] pub struct Output { @@ -17,7 +16,7 @@ pub(crate) fn run(options: Options) -> Result { .collect::>(); let mut balance = 0; - for (outpoint, amount) in index.get_unspent_outputs()? { + for (outpoint, amount) in index.get_unspent_outputs(Wallet::load(&options)?)? { if !inscription_outputs.contains(&outpoint) { balance += amount.to_sat() } diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index ea92b4045d..80a519c8ff 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -1,5 +1,6 @@ use { super::*, + crate::wallet::Wallet, bitcoin::{ blockdata::{opcodes, script}, policy::MAX_STANDARD_TX_WEIGHT, @@ -57,7 +58,7 @@ impl Inscribe { let index = Index::open(&options)?; index.update()?; - let mut utxos = index.get_unspent_outputs()?; + let mut utxos = index.get_unspent_outputs(Wallet::load(&options)?)?; let inscriptions = index.get_inscriptions(None)?; diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 967f086675..73f8a1fc0a 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, crate::wallet::Wallet}; #[derive(Serialize, Deserialize)] pub struct Output { @@ -12,7 +12,7 @@ pub(crate) fn run(options: Options) -> Result { index.update()?; let inscriptions = index.get_inscriptions(None)?; - let unspent_outputs = index.get_unspent_outputs()?; + let unspent_outputs = index.get_unspent_outputs(Wallet::load(&options)?)?; let explorer = match options.chain() { Chain::Mainnet => "https://ordinals.com/inscription/", diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs index c9d92fa57c..d064cd312f 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, crate::wallet::Wallet}; #[derive(Serialize, Deserialize)] pub struct Output { @@ -11,7 +11,7 @@ pub(crate) fn run(options: Options) -> Result { index.update()?; let mut outputs = Vec::new(); - for (output, amount) in index.get_unspent_outputs()? { + for (output, amount) in index.get_unspent_outputs(Wallet::load(&options)?)? { outputs.push(Output { output, amount: amount.to_sat(), diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 15dacb3a45..8006e7477b 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, crate::wallet::Wallet}; #[derive(Debug, Parser)] pub(crate) struct Sats { @@ -28,7 +28,7 @@ impl Sats { let index = Index::open(&options)?; index.update()?; - let utxos = get_unspent_output_ranges(&index)?; + let utxos = index.get_unspent_output_ranges(Wallet::load(&options)?)?; if let Some(path) = &self.tsv { let mut output = Vec::new(); diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 1c18340546..efe4c196df 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, crate::wallet::Wallet}; #[derive(Debug, Parser)] pub(crate) struct Send { @@ -32,7 +32,7 @@ impl Send { let index = Index::open(&options)?; index.update()?; - let unspent_outputs = index.get_unspent_outputs()?; + let unspent_outputs = index.get_unspent_outputs(Wallet::load(&options)?)?; let inscriptions = index.get_inscriptions(None)?; diff --git a/src/wallet.rs b/src/wallet.rs new file mode 100644 index 0000000000..d97435a83f --- /dev/null +++ b/src/wallet.rs @@ -0,0 +1,13 @@ +use super::*; + +pub(crate) struct Wallet { + _private: (), +} + +impl Wallet { + pub(crate) fn load(options: &Options) -> Result { + options.bitcoin_rpc_client_for_wallet_command(false)?; + + Ok(Self { _private: () }) + } +} diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index 44b286f86c..7051d283d6 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -42,7 +42,6 @@ pub fn builder() -> Builder { fail_lock_unspent: false, network: Network::Bitcoin, version: 240000, - wallet_name: "ord", } } @@ -50,7 +49,6 @@ pub struct Builder { fail_lock_unspent: bool, network: Network, version: usize, - wallet_name: &'static str, } impl Builder { @@ -69,18 +67,10 @@ impl Builder { Self { version, ..self } } - pub fn wallet_name(self, wallet_name: &'static str) -> Self { - Self { - wallet_name, - ..self - } - } - pub fn build(self) -> Handle { let state = Arc::new(Mutex::new(State::new( self.network, self.version, - self.wallet_name, self.fail_lock_unspent, ))); let server = Server::new(state.clone()); diff --git a/test-bitcoincore-rpc/src/server.rs b/test-bitcoincore-rpc/src/server.rs index 8721155e53..5e5abc288a 100644 --- a/test-bitcoincore-rpc/src/server.rs +++ b/test-bitcoincore-rpc/src/server.rs @@ -166,23 +166,27 @@ impl Api for Server { } fn get_wallet_info(&self) -> Result { - Ok(GetWalletInfoResult { - avoid_reuse: None, - balance: Amount::from_sat(0), - hd_seed_id: None, - immature_balance: Amount::from_sat(0), - keypool_oldest: None, - keypool_size: 0, - keypool_size_hd_internal: 0, - pay_tx_fee: Amount::from_sat(0), - private_keys_enabled: false, - scanning: None, - tx_count: 0, - unconfirmed_balance: Amount::from_sat(0), - unlocked_until: None, - wallet_name: self.state().wallet_name.clone(), - wallet_version: 0, - }) + if let Some(wallet_name) = self.state().loaded_wallets.first().cloned() { + Ok(GetWalletInfoResult { + avoid_reuse: None, + balance: Amount::from_sat(0), + hd_seed_id: None, + immature_balance: Amount::from_sat(0), + keypool_oldest: None, + keypool_size: 0, + keypool_size_hd_internal: 0, + pay_tx_fee: Amount::from_sat(0), + private_keys_enabled: false, + scanning: None, + tx_count: 0, + unconfirmed_balance: Amount::from_sat(0), + unlocked_until: None, + wallet_name, + wallet_version: 0, + }) + } else { + Err(Self::not_found()) + } } fn create_raw_transaction( diff --git a/test-bitcoincore-rpc/src/state.rs b/test-bitcoincore-rpc/src/state.rs index 68d28a75b2..80f887c003 100644 --- a/test-bitcoincore-rpc/src/state.rs +++ b/test-bitcoincore-rpc/src/state.rs @@ -13,18 +13,12 @@ pub(crate) struct State { pub(crate) transactions: BTreeMap, pub(crate) utxos: BTreeMap, pub(crate) version: usize, - pub(crate) wallet_name: String, pub(crate) wallets: BTreeSet, pub(crate) loaded_wallets: BTreeSet, } impl State { - pub(crate) fn new( - network: Network, - version: usize, - wallet_name: &str, - fail_lock_unspent: bool, - ) -> Self { + pub(crate) fn new(network: Network, version: usize, fail_lock_unspent: bool) -> Self { let mut hashes = Vec::new(); let mut blocks = BTreeMap::new(); @@ -46,7 +40,6 @@ impl State { transactions: BTreeMap::new(), utxos: BTreeMap::new(), version, - wallet_name: wallet_name.to_string(), wallets: BTreeSet::new(), loaded_wallets: BTreeSet::new(), } diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 9e6eabe440..505ecf5d8d 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -140,25 +140,6 @@ fn send_on_mainnnet_works_with_wallet_named_ord() { assert_eq!(format!("{txid}\n"), stdout); } -#[test] -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!( - "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {txid}:0:0" - )) - .rpc_server(&rpc_server) - .stdout_regex(r".*") - .run(); - - let txid = rpc_server.mempool()[0].txid(); - assert_eq!(format!("{txid}\n"), stdout); -} - #[test] fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { let rpc_server = test_bitcoincore_rpc::spawn();