Skip to content

Commit

Permalink
Ensure wallet commands load wallet (#1524)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph authored Feb 7, 2023
1 parent c9619fb commit a9f9e70
Show file tree
Hide file tree
Showing 14 changed files with 84 additions and 83 deletions.
39 changes: 35 additions & 4 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use {
updater::Updater,
},
super::*,
crate::wallet::Wallet,
bitcoin::BlockHeader,
bitcoincore_rpc::{json::GetBlockHeaderResult, Auth, Client},
chrono::SubsecRound,
Expand Down Expand Up @@ -242,7 +243,7 @@ impl Index {
})
}

pub(crate) fn get_unspent_outputs(&self) -> Result<BTreeMap<OutPoint, Amount>> {
pub(crate) fn get_unspent_outputs(&self, _wallet: Wallet) -> Result<BTreeMap<OutPoint, Amount>> {
let mut utxos = BTreeMap::new();
utxos.extend(
self
Expand Down Expand Up @@ -285,6 +286,21 @@ impl Index {
Ok(utxos)
}

pub(crate) fn get_unspent_output_ranges(
&self,
wallet: Wallet,
) -> Result<Vec<(OutPoint, Vec<(u64, u64)>)>> {
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<bool> {
match self.begin_read()?.0.open_table(OUTPOINT_TO_SAT_RANGES) {
Ok(_) => Ok(true),
Expand Down Expand Up @@ -887,7 +903,10 @@ impl Index {

#[cfg(test)]
mod tests {
use super::*;
use {
super::*,
bitcoin::secp256k1::rand::{self, RngCore},
};

struct ContextBuilder {
args: Vec<OsString>,
Expand All @@ -900,7 +919,9 @@ mod tests {
}

fn try_build(self) -> Result<Context> {
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");
Expand All @@ -922,6 +943,7 @@ mod tests {
index.update().unwrap();

Ok(Context {
options,
rpc_server,
tempdir,
index,
Expand All @@ -945,6 +967,7 @@ mod tests {
}

struct Context {
options: Options,
rpc_server: test_bitcoincore_rpc::Handle,
#[allow(unused)]
tempdir: TempDir,
Expand Down Expand Up @@ -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+"
);
}
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ mod sat_point;
pub mod subcommand;
mod tally;
mod templates;
mod wallet;

type Result<T = (), E = Error> = std::result::Result<T, E>;

Expand Down
14 changes: 1 addition & 13 deletions src/subcommand/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,13 @@ impl Wallet {
}
}

fn get_unspent_output_ranges(index: &Index) -> Result<Vec<(OutPoint, Vec<(u64, u64)>)>> {
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<Address> {
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();

Expand Down
5 changes: 2 additions & 3 deletions src/subcommand/wallet/balance.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::*;
use std::collections::BTreeSet;
use {super::*, crate::wallet::Wallet, std::collections::BTreeSet};

#[derive(Serialize, Deserialize)]
pub struct Output {
Expand All @@ -17,7 +16,7 @@ pub(crate) fn run(options: Options) -> Result {
.collect::<BTreeSet<OutPoint>>();

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()
}
Expand Down
3 changes: 2 additions & 1 deletion src/subcommand/wallet/inscribe.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use {
super::*,
crate::wallet::Wallet,
bitcoin::{
blockdata::{opcodes, script},
policy::MAX_STANDARD_TX_WEIGHT,
Expand Down Expand Up @@ -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)?;

Expand Down
4 changes: 2 additions & 2 deletions src/subcommand/wallet/inscriptions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::*;
use {super::*, crate::wallet::Wallet};

#[derive(Serialize, Deserialize)]
pub struct Output {
Expand All @@ -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/",
Expand Down
4 changes: 2 additions & 2 deletions src/subcommand/wallet/outputs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::*;
use {super::*, crate::wallet::Wallet};

#[derive(Serialize, Deserialize)]
pub struct Output {
Expand All @@ -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(),
Expand Down
4 changes: 2 additions & 2 deletions src/subcommand/wallet/sats.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::*;
use {super::*, crate::wallet::Wallet};

#[derive(Debug, Parser)]
pub(crate) struct Sats {
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions src/subcommand/wallet/send.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::*;
use {super::*, crate::wallet::Wallet};

#[derive(Debug, Parser)]
pub(crate) struct Send {
Expand Down Expand Up @@ -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)?;

Expand Down
13 changes: 13 additions & 0 deletions src/wallet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::*;

pub(crate) struct Wallet {
_private: (),
}

impl Wallet {
pub(crate) fn load(options: &Options) -> Result<Self> {
options.bitcoin_rpc_client_for_wallet_command(false)?;

Ok(Self { _private: () })
}
}
10 changes: 0 additions & 10 deletions test-bitcoincore-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,13 @@ pub fn builder() -> Builder {
fail_lock_unspent: false,
network: Network::Bitcoin,
version: 240000,
wallet_name: "ord",
}
}

pub struct Builder {
fail_lock_unspent: bool,
network: Network,
version: usize,
wallet_name: &'static str,
}

impl Builder {
Expand All @@ -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());
Expand Down
38 changes: 21 additions & 17 deletions test-bitcoincore-rpc/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,27 @@ impl Api for Server {
}

fn get_wallet_info(&self) -> Result<GetWalletInfoResult, jsonrpc_core::Error> {
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(
Expand Down
9 changes: 1 addition & 8 deletions test-bitcoincore-rpc/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,12 @@ pub(crate) struct State {
pub(crate) transactions: BTreeMap<Txid, Transaction>,
pub(crate) utxos: BTreeMap<OutPoint, Amount>,
pub(crate) version: usize,
pub(crate) wallet_name: String,
pub(crate) wallets: BTreeSet<String>,
pub(crate) loaded_wallets: BTreeSet<String>,
}

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();

Expand All @@ -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(),
}
Expand Down
19 changes: 0 additions & 19 deletions tests/wallet/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit a9f9e70

Please sign in to comment.