From 7c244c9728cc1e3ef4a926cd79186a2d2c6b5eac Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 31 Dec 2023 16:12:52 +0100 Subject: [PATCH 01/74] Wallet talks to index over http --- Cargo.lock | 4 +- Cargo.toml | 1 + src/subcommand/server.rs | 20 +- src/subcommand/wallet.rs | 327 +++++++++++++++++++----------- src/subcommand/wallet/inscribe.rs | 10 +- 5 files changed, 221 insertions(+), 141 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c2aa97aab..9baeed703f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2589,9 +2589,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "async-compression", "base64 0.21.5", diff --git a/Cargo.toml b/Cargo.toml index c1767aa511..b03621083a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ mp4 = "0.14.0" ord-bitcoincore-rpc = "0.17.1" redb = "1.4.0" regex = "1.6.0" +reqwest = { version = "0.11.23", features = ["blocking", "json"] } rss = "2.0.1" rust-embed = "8.0.0" rustls = "0.22.0" diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 0c5dad2ce7..7a10df60f6 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -131,38 +131,38 @@ pub(crate) struct Server { long, help = "Listen on
for incoming requests. [default: 0.0.0.0]" )] - address: Option, + pub(crate) address: Option, #[arg( long, help = "Request ACME TLS certificate for . This ord instance must be reachable at :443 to respond to Let's Encrypt ACME challenges." )] - acme_domain: Vec, + pub(crate) acme_domain: Vec, #[arg( long, help = "Use in Content-Security-Policy header. Set this to the public-facing URL of your ord instance." )] - csp_origin: Option, + pub(crate) csp_origin: Option, #[arg( long, help = "Listen on for incoming HTTP requests. [default: 80]" )] - http_port: Option, + pub(crate) http_port: Option, #[arg( long, group = "port", help = "Listen on for incoming HTTPS requests. [default: 443]" )] - https_port: Option, + pub(crate) https_port: Option, #[arg(long, help = "Store ACME TLS certificates in .")] - acme_cache: Option, + pub(crate) acme_cache: Option, #[arg(long, help = "Provide ACME contact .")] - acme_contact: Vec, + pub(crate) acme_contact: Vec, #[arg(long, help = "Serve HTTP traffic on .")] - http: bool, + pub(crate) http: bool, #[arg(long, help = "Serve HTTPS traffic on .")] - https: bool, + pub(crate) https: bool, #[arg(long, help = "Redirect HTTP traffic to HTTPS.")] - redirect_http_to_https: bool, + pub(crate) redirect_http_to_https: bool, #[arg(long, short = 'j', help = "Enable JSON API.")] pub(crate) enable_json_api: bool, #[arg( diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 4423626fae..edd030fc14 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -11,6 +11,7 @@ use { bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, fee_rate::FeeRate, miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, + reqwest::{header, Url}, transaction_builder::TransactionBuilder, }; @@ -22,7 +23,7 @@ pub mod inscribe; pub mod inscriptions; pub mod outputs; pub mod receive; -mod restore; +pub mod restore; pub mod sats; pub mod send; pub mod transaction_builder; @@ -66,6 +67,50 @@ pub(crate) enum Subcommand { impl Wallet { pub(crate) fn run(self, options: Options) -> SubcommandResult { + let index = Arc::new(Index::open(&options)?); + let handle = axum_server::Handle::new(); + LISTENERS.lock().unwrap().push(handle.clone()); + + let ord_api_url: Url = format!("127.0.0.1:8080").parse().unwrap(); + + { + let options = options.clone(); + std::thread::spawn(move || { + crate::subcommand::server::Server { + address: ord_api_url.host_str().map(|a| a.to_string()), + acme_domain: vec![], + csp_origin: None, + http_port: ord_api_url.port(), + https_port: None, + acme_cache: None, + acme_contact: vec![], + http: true, + https: false, + redirect_http_to_https: false, + enable_json_api: true, + decompress: false, + } + .run(options, index, handle) + .unwrap() + }); + } + + let wallet_foo = WalletFoo { + bitcoin_rpc_client: bitcoin_rpc_client_for_wallet_command(self.name, &options)?, + ord_api_url, + ord_http_client: { + let mut headers = header::HeaderMap::new(); + headers.insert( + header::ACCEPT, + header::HeaderValue::from_static("application/json"), + ); + let builder = reqwest::blocking::ClientBuilder::new().default_headers(headers); + + builder.build()? + }, + wallet_name: self.name.clone(), + }; + match self.subcommand { Subcommand::Balance => balance::run(self.name, options), Subcommand::Create(create) => create.run(self.name, options), @@ -83,149 +128,183 @@ impl Wallet { } } -pub(crate) fn get_unspent_outputs( - client: &Client, - index: &Index, -) -> Result> { - let mut utxos = BTreeMap::new(); - utxos.extend( - client - .list_unspent(None, None, None, None, None)? - .into_iter() - .map(|utxo| { - let outpoint = OutPoint::new(utxo.txid, utxo.vout); - let amount = utxo.amount; - - (outpoint, amount) - }), - ); - - let locked_utxos: BTreeSet = get_locked_outputs(client)?; - - for outpoint in locked_utxos { - utxos.insert( - outpoint, - Amount::from_sat( - client.get_raw_transaction(&outpoint.txid, None)?.output - [TryInto::::try_into(outpoint.vout).unwrap()] - .value, - ), +pub(crate) struct WalletFoo { + pub(crate) bitcoin_rpc_client: Client, + pub(crate) ord_api_url: Url, + // TODO: make this async + pub(crate) ord_http_client: reqwest::blocking::Client, + pub(crate) wallet_name: String, +} + +impl WalletFoo { + pub(crate) fn get_unspent_outputs(&self) -> Result> { + let mut utxos = BTreeMap::new(); + utxos.extend( + self + .bitcoin_rpc_client + .list_unspent(None, None, None, None, None)? + .into_iter() + .map(|utxo| { + let outpoint = OutPoint::new(utxo.txid, utxo.vout); + let amount = utxo.amount; + + (outpoint, amount) + }), ); - } - index.check_sync(&utxos)?; + let locked_utxos: BTreeSet = self.get_locked_outputs()?; + + for outpoint in locked_utxos { + utxos.insert( + outpoint, + Amount::from_sat( + self + .bitcoin_rpc_client + .get_raw_transaction(&outpoint.txid, None)? + .output[TryInto::::try_into(outpoint.vout).unwrap()] + .value, + ), + ); + } - Ok(utxos) -} + for outpoint in utxos.keys() { + if self + .ord_http_client + .get( + self + .ord_api_url + .join(&format!("/output/{outpoint}")) + .unwrap(), + ) + .send() + .unwrap() + .status() + .is_client_error() + { + return Err(anyhow!( + "output in Bitcoin Core wallet but not in ord index: {outpoint}" + )); + } + } -pub(crate) fn get_unspent_output_ranges( - client: &Client, - index: &Index, -) -> Result)>> { - get_unspent_outputs(client, index)? - .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() -} + Ok(utxos) + } -pub(crate) fn get_locked_outputs(client: &Client) -> Result> { - #[derive(Deserialize)] - pub(crate) struct JsonOutPoint { - txid: bitcoin::Txid, - vout: u32, + pub(crate) fn get_unspent_output_ranges( + &self, + index: &Index, + ) -> Result)>> { + self + .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() } - Ok( - client - .call::>("listlockunspent", &[])? - .into_iter() - .map(|outpoint| OutPoint::new(outpoint.txid, outpoint.vout)) - .collect(), - ) -} + pub(crate) fn get_locked_outputs(&self) -> Result> { + #[derive(Deserialize)] + pub(crate) struct JsonOutPoint { + txid: bitcoin::Txid, + vout: u32, + } -pub(crate) fn get_change_address(client: &Client, chain: Chain) -> Result
{ - Ok( - client - .call::>("getrawchangeaddress", &["bech32m".into()]) - .context("could not get change addresses from wallet")? - .require_network(chain.network())?, - ) -} + Ok( + self + .bitcoin_rpc_client + .call::>("listlockunspent", &[])? + .into_iter() + .map(|outpoint| OutPoint::new(outpoint.txid, outpoint.vout)) + .collect(), + ) + } -pub(crate) fn initialize(wallet: String, options: &Options, seed: [u8; 64]) -> Result { - let client = check_version(options.bitcoin_rpc_client(None)?)?; + pub(crate) fn get_change_address(&self, chain: Chain) -> Result
{ + Ok( + self + .bitcoin_rpc_client + .call::>("getrawchangeaddress", &["bech32m".into()]) + .context("could not get change addresses from wallet")? + .require_network(chain.network())?, + ) + } - let network = options.chain().network(); + pub(crate) fn initialize(&self, wallet: String, options: &Options, seed: [u8; 64]) -> Result { + let client = check_version(options.bitcoin_rpc_client(None)?)?; - client.create_wallet(&wallet, None, Some(true), None, None)?; + let network = options.chain().network(); - let secp = Secp256k1::new(); + &self + .bitcoin_rpc_client + .create_wallet(&wallet, None, Some(true), None, None)?; - let master_private_key = ExtendedPrivKey::new_master(network, &seed)?; + let secp = Secp256k1::new(); - let fingerprint = master_private_key.fingerprint(&secp); + let master_private_key = ExtendedPrivKey::new_master(network, &seed)?; - let derivation_path = DerivationPath::master() - .child(ChildNumber::Hardened { index: 86 }) - .child(ChildNumber::Hardened { - index: u32::from(network != Network::Bitcoin), - }) - .child(ChildNumber::Hardened { index: 0 }); + let fingerprint = master_private_key.fingerprint(&secp); - let derived_private_key = master_private_key.derive_priv(&secp, &derivation_path)?; + let derivation_path = DerivationPath::master() + .child(ChildNumber::Hardened { index: 86 }) + .child(ChildNumber::Hardened { + index: u32::from(network != Network::Bitcoin), + }) + .child(ChildNumber::Hardened { index: 0 }); + + let derived_private_key = master_private_key.derive_priv(&secp, &derivation_path)?; + + for change in [false, true] { + self.derive_and_import_descriptor( + &secp, + (fingerprint, derivation_path.clone()), + derived_private_key, + change, + )?; + } - for change in [false, true] { - derive_and_import_descriptor( - &client, - &secp, - (fingerprint, derivation_path.clone()), - derived_private_key, - change, - )?; + Ok(()) } - Ok(()) -} + fn derive_and_import_descriptor( + &self, + secp: &Secp256k1, + origin: (Fingerprint, DerivationPath), + derived_private_key: ExtendedPrivKey, + change: bool, + ) -> Result { + let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { + origin: Some(origin), + xkey: derived_private_key, + derivation_path: DerivationPath::master().child(ChildNumber::Normal { + index: change.into(), + }), + wildcard: Wildcard::Unhardened, + }); + + let public_key = secret_key.to_public(secp)?; + + let mut key_map = std::collections::HashMap::new(); + key_map.insert(public_key.clone(), secret_key); -fn derive_and_import_descriptor( - client: &Client, - secp: &Secp256k1, - origin: (Fingerprint, DerivationPath), - derived_private_key: ExtendedPrivKey, - change: bool, -) -> Result { - let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some(origin), - xkey: derived_private_key, - derivation_path: DerivationPath::master().child(ChildNumber::Normal { - index: change.into(), - }), - wildcard: Wildcard::Unhardened, - }); - - let public_key = secret_key.to_public(secp)?; - - let mut key_map = std::collections::HashMap::new(); - key_map.insert(public_key.clone(), secret_key); - - let desc = Descriptor::new_tr(public_key, 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: Some(change), - label: None, - })?; - - Ok(()) + let desc = Descriptor::new_tr(public_key, None)?; + + self + .bitcoin_rpc_client + .import_descriptors(ImportDescriptors { + descriptor: desc.to_string_with_secret(&key_map), + timestamp: Timestamp::Now, + active: Some(true), + range: None, + next_index: None, + internal: Some(change), + label: None, + })?; + + Ok(()) + } } pub(crate) fn bitcoin_rpc_client_for_wallet_command( diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 71dc9b039a..b9c860bc42 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -108,17 +108,17 @@ pub(crate) struct Inscribe { } impl Inscribe { - pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: WalletFoo, options: Options) -> SubcommandResult { let metadata = Inscribe::parse_metadata(self.cbor_metadata, self.json_metadata)?; let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = wallet.bitcoin_rpc_client; - let utxos = get_unspent_outputs(&client, &index)?; + let utxos = wallet.get_unspent_outputs()?; - let locked_utxos = get_locked_outputs(&client)?; + let locked_utxos = wallet.get_locked_outputs()?; let runic_utxos = index.get_runic_outputs(&utxos.keys().cloned().collect::>())?; @@ -153,7 +153,7 @@ impl Inscribe { destinations = vec![match self.destination.clone() { Some(destination) => destination.require_network(chain.network())?, - None => get_change_address(&client, chain)?, + None => wallet.get_change_address(chain)?, }]; } (None, Some(batch)) => { From e0efc59bdbd9b974c39db8ac837a26e13ea05ef1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 2 Jan 2024 01:37:11 +0100 Subject: [PATCH 02/74] Fix test --- src/index.rs | 54 ++++++++++++++------------- src/options.rs | 2 +- src/subcommand.rs | 2 +- src/subcommand/preview.rs | 19 +++++++--- src/subcommand/wallet.rs | 27 +++++++------- src/subcommand/wallet/balance.rs | 2 +- src/subcommand/wallet/cardinals.rs | 2 +- src/subcommand/wallet/create.rs | 4 +- src/subcommand/wallet/etch.rs | 2 +- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/inscriptions.rs | 2 +- src/subcommand/wallet/outputs.rs | 2 +- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/restore.rs | 8 +--- src/subcommand/wallet/sats.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/subcommand/wallet/transactions.rs | 2 +- 17 files changed, 71 insertions(+), 65 deletions(-) diff --git a/src/index.rs b/src/index.rs index 20c7bd0740..37d56dd99f 100644 --- a/src/index.rs +++ b/src/index.rs @@ -381,20 +381,6 @@ impl Index { self.durability = durability; } - pub(crate) fn check_sync(&self, utxos: &BTreeMap) -> Result { - let rtx = self.database.begin_read()?; - let outpoint_to_value = rtx.open_table(OUTPOINT_TO_VALUE)?; - for outpoint in utxos.keys() { - if outpoint_to_value.get(&outpoint.store())?.is_none() { - return Err(anyhow!( - "output in Bitcoin Core wallet but not in ord index: {outpoint}" - )); - } - } - - Ok(true) - } - pub(crate) fn has_rune_index(&self) -> bool { self.index_runes } @@ -3394,20 +3380,36 @@ mod tests { let mut entropy = [0; 16]; rand::thread_rng().fill_bytes(&mut entropy); let mnemonic = Mnemonic::from_entropy(&entropy).unwrap(); - crate::subcommand::wallet::initialize("ord".into(), &context.options, mnemonic.to_seed("")) - .unwrap(); + + Arguments { + options: context.options.clone(), + subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + name: "ord".into(), + subcommand: crate::subcommand::wallet::Subcommand::Create( + crate::subcommand::wallet::create::Create { + passphrase: "".into(), + }, + ), + }), + } + .run() + .unwrap(); + context.rpc_server.mine_blocks(1); - assert_regex_match!( - crate::subcommand::wallet::get_unspent_outputs( - &crate::subcommand::wallet::bitcoin_rpc_client_for_wallet_command( - "ord".to_string(), - &context.options, - ) - .unwrap(), - &context.index + + let wallet = crate::subcommand::wallet::Wallet { + bitcoin_rpc_client: crate::subcommand::wallet::bitcoin_rpc_client_for_wallet( + "ord".into(), + &context.options, ) - .unwrap_err() - .to_string(), + .unwrap(), + ord_api_url: "127.0.0.1:8080".parse().unwrap(), + ord_http_client: reqwest::blocking::Client::new(), + wallet_name: "ord".into(), + }; + + assert_regex_match!( + wallet.get_unspent_outputs().unwrap_err().to_string(), r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" ); } diff --git a/src/options.rs b/src/options.rs index ec6261f0f8..fc03adb9dd 100644 --- a/src/options.rs +++ b/src/options.rs @@ -567,7 +567,7 @@ mod tests { ); } - fn parse_wallet_args(args: &str) -> (Options, subcommand::wallet::Wallet) { + fn parse_wallet_args(args: &str) -> (Options, subcommand::wallet::WalletCommand) { match Arguments::try_parse_from(args.split_whitespace()) { Ok(arguments) => match arguments.subcommand { Subcommand::Wallet(wallet) => (arguments.options, wallet), diff --git a/src/subcommand.rs b/src/subcommand.rs index ff7b406079..9aff172e0e 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -47,7 +47,7 @@ pub(crate) enum Subcommand { #[command(about = "Display satoshi traits")] Traits(traits::Traits), #[command(about = "Wallet commands")] - Wallet(wallet::Wallet), + Wallet(wallet::WalletCommand), } impl Subcommand { diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 845c0c90d6..77e9f96861 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -83,10 +83,19 @@ impl Preview { thread::sleep(Duration::from_millis(50)); } - super::wallet::create::Create { - passphrase: "".into(), + Arguments { + options: options.clone(), + subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + name: "ord".into(), + subcommand: crate::subcommand::wallet::Subcommand::Create( + crate::subcommand::wallet::create::Create { + passphrase: "".into(), + }, + ), + }), } - .run("ord".into(), options.clone())?; + .run() + .unwrap(); let rpc_client = options.bitcoin_rpc_client(None)?; @@ -102,7 +111,7 @@ impl Preview { for file in files { Arguments { options: options.clone(), - subcommand: Subcommand::Wallet(super::wallet::Wallet { + subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: None, @@ -135,7 +144,7 @@ impl Preview { for batch in batches { Arguments { options: options.clone(), - subcommand: Subcommand::Wallet(super::wallet::Wallet { + subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: Some(batch), diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index edd030fc14..00444c94a8 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -30,7 +30,7 @@ pub mod transaction_builder; pub mod transactions; #[derive(Debug, Parser)] -pub(crate) struct Wallet { +pub(crate) struct WalletCommand { #[arg(long, default_value = "ord", help = "Use wallet named .")] pub(crate) name: String, #[command(subcommand)] @@ -65,7 +65,7 @@ pub(crate) enum Subcommand { Cardinals, } -impl Wallet { +impl WalletCommand { pub(crate) fn run(self, options: Options) -> SubcommandResult { let index = Arc::new(Index::open(&options)?); let handle = axum_server::Handle::new(); @@ -95,8 +95,8 @@ impl Wallet { }); } - let wallet_foo = WalletFoo { - bitcoin_rpc_client: bitcoin_rpc_client_for_wallet_command(self.name, &options)?, + let wallet = Wallet { + bitcoin_rpc_client: bitcoin_rpc_client_for_wallet(self.name, &options)?, ord_api_url, ord_http_client: { let mut headers = header::HeaderMap::new(); @@ -113,12 +113,12 @@ impl Wallet { match self.subcommand { Subcommand::Balance => balance::run(self.name, options), - Subcommand::Create(create) => create.run(self.name, options), + Subcommand::Create(create) => create.run(wallet, options), Subcommand::Etch(etch) => etch.run(self.name, options), - Subcommand::Inscribe(inscribe) => inscribe.run(self.name, options), + Subcommand::Inscribe(inscribe) => inscribe.run(wallet, options), Subcommand::Inscriptions => inscriptions::run(self.name, options), Subcommand::Receive => receive::run(self.name, options), - Subcommand::Restore(restore) => restore.run(self.name, options), + Subcommand::Restore(restore) => restore.run(wallet, options), Subcommand::Sats(sats) => sats.run(self.name, options), Subcommand::Send(send) => send.run(self.name, options), Subcommand::Transactions(transactions) => transactions.run(self.name, options), @@ -128,15 +128,14 @@ impl Wallet { } } -pub(crate) struct WalletFoo { +pub(crate) struct Wallet { pub(crate) bitcoin_rpc_client: Client, pub(crate) ord_api_url: Url, - // TODO: make this async - pub(crate) ord_http_client: reqwest::blocking::Client, + pub(crate) ord_http_client: reqwest::blocking::Client, // TODO: make async instead of blocking pub(crate) wallet_name: String, } -impl WalletFoo { +impl Wallet { pub(crate) fn get_unspent_outputs(&self) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( @@ -232,14 +231,14 @@ impl WalletFoo { ) } - pub(crate) fn initialize(&self, wallet: String, options: &Options, seed: [u8; 64]) -> Result { + pub(crate) fn initialize(&self, options: &Options, seed: [u8; 64]) -> Result { let client = check_version(options.bitcoin_rpc_client(None)?)?; let network = options.chain().network(); &self .bitcoin_rpc_client - .create_wallet(&wallet, None, Some(true), None, None)?; + .create_wallet(&self.wallet_name, None, Some(true), None, None)?; let secp = Secp256k1::new(); @@ -307,7 +306,7 @@ impl WalletFoo { } } -pub(crate) fn bitcoin_rpc_client_for_wallet_command( +pub(crate) fn bitcoin_rpc_client_for_wallet( wallet_name: String, options: &Options, ) -> Result { diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index 8a95985e44..01dee24102 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -15,7 +15,7 @@ pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let unspent_outputs = get_unspent_outputs(&client, &index)?; diff --git a/src/subcommand/wallet/cardinals.rs b/src/subcommand/wallet/cardinals.rs index ab6e932521..6622b69cfd 100644 --- a/src/subcommand/wallet/cardinals.rs +++ b/src/subcommand/wallet/cardinals.rs @@ -11,7 +11,7 @@ pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let unspent_outputs = get_unspent_outputs(&client, &index)?; diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 6b8fb9b0a0..2cedcdbd8f 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -17,13 +17,13 @@ pub(crate) struct Create { } impl Create { - pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { let mut entropy = [0; 16]; rand::thread_rng().fill_bytes(&mut entropy); let mnemonic = Mnemonic::from_entropy(&entropy)?; - wallet::initialize(wallet, &options, mnemonic.to_seed(self.passphrase.clone()))?; + wallet.initialize(&options, mnemonic.to_seed(self.passphrase.clone()))?; Ok(Box::new(Output { mnemonic, diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index b02ea68015..0f0ed07a24 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -30,7 +30,7 @@ impl Etch { index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let SpacedRune { rune, spacers } = self.rune; diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index b9c860bc42..e305c79e29 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -108,7 +108,7 @@ pub(crate) struct Inscribe { } impl Inscribe { - pub(crate) fn run(self, wallet: WalletFoo, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { let metadata = Inscribe::parse_metadata(self.cbor_metadata, self.json_metadata)?; let index = Index::open(&options)?; diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 5cf2c682f8..9b2f4b9825 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -12,7 +12,7 @@ pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let unspent_outputs = get_unspent_outputs(&client, &index)?; diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs index 9c4655b61a..4b650e2bad 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -11,7 +11,7 @@ pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let mut outputs = Vec::new(); for (output, amount) in get_unspent_outputs(&client, &index)? { diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 2ff8afc481..42f40ea58e 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -6,7 +6,7 @@ pub struct Output { } pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { - let address = bitcoin_rpc_client_for_wallet_command(wallet, &options)? + let address = bitcoin_rpc_client_for_wallet(wallet, &options)? .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; Ok(Box::new(Output { address })) diff --git a/src/subcommand/wallet/restore.rs b/src/subcommand/wallet/restore.rs index d4e48b6b78..6b838d246a 100644 --- a/src/subcommand/wallet/restore.rs +++ b/src/subcommand/wallet/restore.rs @@ -13,12 +13,8 @@ pub(crate) struct Restore { } impl Restore { - pub(crate) fn run(self, wallet_name: String, options: Options) -> SubcommandResult { - wallet::initialize( - wallet_name, - &options, - self.mnemonic.to_seed(self.passphrase), - )?; + pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { + wallet.initialize(&options, self.mnemonic.to_seed(self.passphrase))?; Ok(Box::new(Empty {})) } diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 926610f9b8..050e1620bc 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -33,7 +33,7 @@ impl Sats { index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let utxos = get_unspent_output_ranges(&client, &index)?; diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 24575ceac2..cf61c891ce 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -29,7 +29,7 @@ impl Send { index.update()?; - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let chain = options.chain(); diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 8c11410127..fc77370cba 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -14,7 +14,7 @@ pub struct Output { impl Transactions { pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { - let client = bitcoin_rpc_client_for_wallet_command(wallet, &options)?; + let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; let mut output = Vec::new(); for tx in client.list_transactions( From bf5d45172cb6b2d919093919ff67770f896333e4 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 2 Jan 2024 02:24:07 +0100 Subject: [PATCH 03/74] It compiles --- src/index.rs | 1 - src/subcommand/wallet.rs | 27 ++++++------ src/subcommand/wallet/balance.rs | 6 +-- src/subcommand/wallet/cardinals.rs | 6 +-- src/subcommand/wallet/etch.rs | 25 ++++++----- src/subcommand/wallet/inscribe.rs | 15 +++---- src/subcommand/wallet/inscribe/batch.rs | 57 +++++++++++++++---------- src/subcommand/wallet/inscriptions.rs | 6 +-- src/subcommand/wallet/outputs.rs | 6 +-- src/subcommand/wallet/receive.rs | 5 ++- src/subcommand/wallet/sats.rs | 6 +-- src/subcommand/wallet/send.rs | 49 +++++++++++---------- src/subcommand/wallet/transactions.rs | 4 +- 13 files changed, 111 insertions(+), 102 deletions(-) diff --git a/src/index.rs b/src/index.rs index 37d56dd99f..ed53b132ce 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3379,7 +3379,6 @@ mod tests { for context in Context::configurations() { let mut entropy = [0; 16]; rand::thread_rng().fill_bytes(&mut entropy); - let mnemonic = Mnemonic::from_entropy(&entropy).unwrap(); Arguments { options: context.options.clone(), diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 00444c94a8..4eecf57ad7 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -71,10 +71,11 @@ impl WalletCommand { let handle = axum_server::Handle::new(); LISTENERS.lock().unwrap().push(handle.clone()); - let ord_api_url: Url = format!("127.0.0.1:8080").parse().unwrap(); + let ord_api_url: Url = format!("http://127.0.0.1:8080").parse().unwrap(); { let options = options.clone(); + let ord_api_url = ord_api_url.clone(); std::thread::spawn(move || { crate::subcommand::server::Server { address: ord_api_url.host_str().map(|a| a.to_string()), @@ -96,7 +97,7 @@ impl WalletCommand { } let wallet = Wallet { - bitcoin_rpc_client: bitcoin_rpc_client_for_wallet(self.name, &options)?, + bitcoin_rpc_client: bitcoin_rpc_client_for_wallet(self.name.clone(), &options)?, ord_api_url, ord_http_client: { let mut headers = header::HeaderMap::new(); @@ -112,18 +113,18 @@ impl WalletCommand { }; match self.subcommand { - Subcommand::Balance => balance::run(self.name, options), + Subcommand::Balance => balance::run(wallet, options), Subcommand::Create(create) => create.run(wallet, options), - Subcommand::Etch(etch) => etch.run(self.name, options), + Subcommand::Etch(etch) => etch.run(wallet, options), Subcommand::Inscribe(inscribe) => inscribe.run(wallet, options), - Subcommand::Inscriptions => inscriptions::run(self.name, options), - Subcommand::Receive => receive::run(self.name, options), + Subcommand::Inscriptions => inscriptions::run(wallet, options), + Subcommand::Receive => receive::run(wallet), Subcommand::Restore(restore) => restore.run(wallet, options), - Subcommand::Sats(sats) => sats.run(self.name, options), - Subcommand::Send(send) => send.run(self.name, options), - Subcommand::Transactions(transactions) => transactions.run(self.name, options), - Subcommand::Outputs => outputs::run(self.name, options), - Subcommand::Cardinals => cardinals::run(self.name, options), + Subcommand::Sats(sats) => sats.run(wallet, options), + Subcommand::Send(send) => send.run(wallet, options), + Subcommand::Transactions(transactions) => transactions.run(wallet), + Subcommand::Outputs => outputs::run(wallet, options), + Subcommand::Cardinals => cardinals::run(wallet, options), } } } @@ -232,11 +233,11 @@ impl Wallet { } pub(crate) fn initialize(&self, options: &Options, seed: [u8; 64]) -> Result { - let client = check_version(options.bitcoin_rpc_client(None)?)?; + let _client = check_version(options.bitcoin_rpc_client(None)?)?; let network = options.chain().network(); - &self + let _ = &self .bitcoin_rpc_client .create_wallet(&self.wallet_name, None, Some(true), None, None)?; diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index 01dee24102..a4ca9092e5 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -11,13 +11,11 @@ pub struct Output { pub total: u64, } -pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { +pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - - let unspent_outputs = get_unspent_outputs(&client, &index)?; + let unspent_outputs = wallet.get_unspent_outputs()?; let inscription_outputs = index .get_inscriptions(&unspent_outputs)? diff --git a/src/subcommand/wallet/cardinals.rs b/src/subcommand/wallet/cardinals.rs index 6622b69cfd..0a13006ad7 100644 --- a/src/subcommand/wallet/cardinals.rs +++ b/src/subcommand/wallet/cardinals.rs @@ -6,14 +6,12 @@ pub struct CardinalUtxo { pub amount: u64, } -pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { +pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - - let unspent_outputs = get_unspent_outputs(&client, &index)?; + let unspent_outputs = wallet.get_unspent_outputs()?; let inscribed_utxos = index .get_inscriptions(&unspent_outputs)? diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 0f0ed07a24..efa47e6d3e 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -20,7 +20,7 @@ pub struct Output { } impl Etch { - pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { let index = Index::open(&options)?; ensure!( @@ -30,11 +30,9 @@ impl Etch { index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - let SpacedRune { rune, spacers } = self.rune; - let count = client.get_block_count()?; + let count = wallet.bitcoin_rpc_client.get_block_count()?; ensure!( index.rune(rune)?.is_none(), @@ -58,7 +56,7 @@ impl Etch { " must be equal to or less than 38" ); - let destination = get_change_address(&client, options.chain())?; + let destination = wallet.get_change_address(options.chain())?; let runestone = Runestone { etching: Some(Etching { @@ -103,7 +101,7 @@ impl Etch { ], }; - let unspent_outputs = get_unspent_outputs(&client, &index)?; + let unspent_outputs = wallet.get_unspent_outputs()?; let inscriptions = index .get_inscriptions(&unspent_outputs)? @@ -111,17 +109,24 @@ impl Etch { .map(|satpoint| satpoint.outpoint) .collect::>(); - if !client.lock_unspent(&inscriptions)? { + if !wallet.bitcoin_rpc_client.lock_unspent(&inscriptions)? { bail!("failed to lock UTXOs"); } - let unsigned_transaction = fund_raw_transaction(&client, self.fee_rate, &unfunded_transaction)?; + let unsigned_transaction = fund_raw_transaction( + &wallet.bitcoin_rpc_client, + self.fee_rate, + &unfunded_transaction, + )?; - let signed_transaction = client + let signed_transaction = wallet + .bitcoin_rpc_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let transaction = client.send_raw_transaction(&signed_transaction)?; + let transaction = wallet + .bitcoin_rpc_client + .send_raw_transaction(&signed_transaction)?; Ok(Box::new(Output { transaction })) } diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index e305c79e29..ffca2e9da8 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -13,7 +13,6 @@ use { taproot::{ControlBlock, LeafVersion, TapLeafHash, TaprootBuilder}, }, bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, SignRawTransactionInput, Timestamp}, - bitcoincore_rpc::Client, }; mod batch; @@ -114,8 +113,6 @@ impl Inscribe { let index = Index::open(&options)?; index.update()?; - let client = wallet.bitcoin_rpc_client; - let utxos = wallet.get_unspent_outputs()?; let locked_utxos = wallet.get_locked_outputs()?; @@ -133,7 +130,7 @@ impl Inscribe { match (self.file, self.batch) { (Some(file), None) => { - parent_info = Inscribe::get_parent_info(self.parent, &index, &utxos, &client, chain)?; + parent_info = Inscribe::get_parent_info(self.parent, &index, &utxos, chain, &wallet)?; postage = self.postage.unwrap_or(TARGET_POSTAGE); @@ -159,7 +156,7 @@ impl Inscribe { (None, Some(batch)) => { let batchfile = Batchfile::load(&batch)?; - parent_info = Inscribe::get_parent_info(batchfile.parent, &index, &utxos, &client, chain)?; + parent_info = Inscribe::get_parent_info(batchfile.parent, &index, &utxos, chain, &wallet)?; postage = batchfile .postage @@ -167,12 +164,12 @@ impl Inscribe { .unwrap_or(TARGET_POSTAGE); (inscriptions, destinations) = batchfile.inscriptions( - &client, chain, parent_info.as_ref().map(|info| info.tx_out.value), metadata, postage, self.compress, + &wallet, )?; mode = batchfile.mode; @@ -214,7 +211,7 @@ impl Inscribe { reveal_fee_rate: self.fee_rate, satpoint, } - .inscribe(chain, &index, &client, &locked_utxos, runic_utxos, &utxos) + .inscribe(chain, &index, &locked_utxos, runic_utxos, &utxos, &wallet) } fn parse_metadata(cbor: Option, json: Option) -> Result>> { @@ -240,8 +237,8 @@ impl Inscribe { parent: Option, index: &Index, utxos: &BTreeMap, - client: &Client, chain: Chain, + wallet: &Wallet, ) -> Result> { if let Some(parent_id) = parent { if let Some(satpoint) = index.get_inscription_satpoint_by_id(parent_id)? { @@ -250,7 +247,7 @@ impl Inscribe { } Ok(Some(ParentInfo { - destination: get_change_address(client, chain)?, + destination: wallet.get_change_address(chain)?, id: parent_id, location: satpoint, tx_out: index diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index 1024097825..08fb8042fd 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -39,16 +39,16 @@ impl Batch { &self, chain: Chain, index: &Index, - client: &Client, locked_utxos: &BTreeSet, runic_utxos: BTreeSet, utxos: &BTreeMap, + wallet: &Wallet, ) -> SubcommandResult { let wallet_inscriptions = index.get_inscriptions(utxos)?; let commit_tx_change = [ - get_change_address(client, chain)?, - get_change_address(client, chain)?, + wallet.get_change_address(chain)?, + wallet.get_change_address(chain)?, ]; let (commit_tx, reveal_tx, recovery_key_pair, total_fees) = self @@ -70,12 +70,14 @@ impl Batch { ))); } - let signed_commit_tx = client + let signed_commit_tx = wallet + .bitcoin_rpc_client .sign_raw_transaction_with_wallet(&commit_tx, None, None)? .hex; let signed_reveal_tx = if self.parent_info.is_some() { - client + wallet + .bitcoin_rpc_client .sign_raw_transaction_with_wallet( &reveal_tx, Some( @@ -100,12 +102,17 @@ impl Batch { }; if !self.no_backup { - Self::backup_recovery_key(client, recovery_key_pair, chain.network())?; + Self::backup_recovery_key(&wallet, recovery_key_pair, chain.network())?; } - let commit = client.send_raw_transaction(&signed_commit_tx)?; + let commit = wallet + .bitcoin_rpc_client + .send_raw_transaction(&signed_commit_tx)?; - let reveal = match client.send_raw_transaction(&signed_reveal_tx) { + let reveal = match wallet + .bitcoin_rpc_client + .send_raw_transaction(&signed_reveal_tx) + { Ok(txid) => txid, Err(err) => { return Err(anyhow!( @@ -445,23 +452,27 @@ impl Batch { } fn backup_recovery_key( - client: &Client, + wallet: &Wallet, recovery_key_pair: TweakedKeyPair, network: Network, ) -> Result { let recovery_private_key = PrivateKey::new(recovery_key_pair.to_inner().secret_key(), network); - let info = client.get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; - - 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("commit tx recovery key".to_string()), - })?; + let info = wallet + .bitcoin_rpc_client + .get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; + + let response = wallet + .bitcoin_rpc_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("commit tx recovery key".to_string()), + })?; for result in response { if !result.success { @@ -585,12 +596,12 @@ impl Batchfile { pub(crate) fn inscriptions( &self, - client: &Client, chain: Chain, parent_value: Option, metadata: Option>, postage: Amount, compress: bool, + wallet: &Wallet, ) -> Result<(Vec, Vec
)> { assert!(!self.inscriptions.is_empty()); @@ -633,13 +644,13 @@ impl Batchfile { } let destinations = match self.mode { - Mode::SharedOutput | Mode::SameSat => vec![get_change_address(client, chain)?], + Mode::SharedOutput | Mode::SameSat => vec![wallet.get_change_address(chain)?], Mode::SeparateOutputs => self .inscriptions .iter() .map(|entry| { entry.destination.as_ref().map_or_else( - || get_change_address(client, chain), + || wallet.get_change_address(chain), |address| { address .clone() diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 9b2f4b9825..1c08021c94 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -8,13 +8,11 @@ pub struct Output { pub postage: u64, } -pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { +pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - - let unspent_outputs = get_unspent_outputs(&client, &index)?; + let unspent_outputs = wallet.get_unspent_outputs()?; let inscriptions = index.get_inscriptions(&unspent_outputs)?; diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs index 4b650e2bad..d1d9a8d4c4 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -6,15 +6,13 @@ pub struct Output { pub amount: u64, } -pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { +pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { let index = Index::open(&options)?; index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - let mut outputs = Vec::new(); - for (output, amount) in get_unspent_outputs(&client, &index)? { + for (output, amount) in wallet.get_unspent_outputs()? { outputs.push(Output { output, amount: amount.to_sat(), diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 42f40ea58e..31d12b19fa 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -5,8 +5,9 @@ pub struct Output { pub address: Address, } -pub(crate) fn run(wallet: String, options: Options) -> SubcommandResult { - let address = bitcoin_rpc_client_for_wallet(wallet, &options)? +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let address = wallet + .bitcoin_rpc_client .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; Ok(Box::new(Output { address })) diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 050e1620bc..860176e927 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -24,7 +24,7 @@ pub struct OutputRare { } impl Sats { - pub(crate) fn run(&self, wallet: String, options: Options) -> SubcommandResult { + pub(crate) fn run(&self, wallet: Wallet, options: Options) -> SubcommandResult { let index = Index::open(&options)?; if !index.has_sat_index() { @@ -33,9 +33,7 @@ impl Sats { index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - - let utxos = get_unspent_output_ranges(&client, &index)?; + let utxos = wallet.get_unspent_output_ranges(&index)?; 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 cf61c891ce..201dc28033 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -19,7 +19,7 @@ pub struct Output { } impl Send { - pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { let address = self .address .clone() @@ -29,13 +29,11 @@ impl Send { index.update()?; - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; - let chain = options.chain(); - let unspent_outputs = get_unspent_outputs(&client, &index)?; + let unspent_outputs = wallet.get_unspent_outputs()?; - let locked_outputs = get_locked_outputs(&client)?; + let locked_outputs = wallet.get_locked_outputs()?; let inscriptions = index.get_inscriptions(&unspent_outputs)?; @@ -44,8 +42,8 @@ impl Send { let satpoint = match self.outgoing { Outgoing::Amount(amount) => { - Self::lock_non_cardinal_outputs(&client, &inscriptions, &runic_outputs, unspent_outputs)?; - let transaction = Self::send_amount(&client, amount, address, self.fee_rate)?; + Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; + let transaction = Self::send_amount(&wallet, amount, address, self.fee_rate)?; return Ok(Box::new(Output { transaction })); } Outgoing::InscriptionId(id) => index @@ -55,7 +53,6 @@ impl Send { let transaction = Self::send_runes( address, chain, - &client, decimal, self.fee_rate, &index, @@ -63,6 +60,7 @@ impl Send { rune, runic_outputs, unspent_outputs, + &wallet, )?; return Ok(Box::new(Output { transaction })); } @@ -83,8 +81,8 @@ impl Send { }; let change = [ - get_change_address(&client, chain)?, - get_change_address(&client, chain)?, + wallet.get_change_address(chain)?, + wallet.get_change_address(chain)?, ]; let postage = if let Some(postage) = self.postage { @@ -106,17 +104,18 @@ impl Send { ) .build_transaction()?; - let signed_tx = client + let signed_tx = wallet + .bitcoin_rpc_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let txid = client.send_raw_transaction(&signed_tx)?; + let txid = wallet.bitcoin_rpc_client.send_raw_transaction(&signed_tx)?; Ok(Box::new(Output { transaction: txid })) } fn lock_non_cardinal_outputs( - client: &Client, + wallet: &Wallet, inscriptions: &BTreeMap, runic_outputs: &BTreeSet, unspent_outputs: BTreeMap, @@ -133,7 +132,7 @@ impl Send { .cloned() .collect::>(); - if !client.lock_unspent(&locked_outputs)? { + if !wallet.bitcoin_rpc_client.lock_unspent(&locked_outputs)? { bail!("failed to lock UTXOs"); } @@ -141,12 +140,12 @@ impl Send { } fn send_amount( - client: &Client, + wallet: &Wallet, amount: Amount, address: Address, fee_rate: FeeRate, ) -> Result { - Ok(client.call( + Ok(wallet.bitcoin_rpc_client.call( "sendtoaddress", &[ address.to_string().into(), // 1. address @@ -166,7 +165,6 @@ impl Send { fn send_runes( address: Address, chain: Chain, - client: &Client, decimal: Decimal, fee_rate: FeeRate, index: &Index, @@ -174,13 +172,14 @@ impl Send { spaced_rune: SpacedRune, runic_outputs: BTreeSet, unspent_outputs: BTreeMap, + wallet: &Wallet, ) -> Result { ensure!( index.has_rune_index(), "sending runes with `ord send` requires index created with `--index-runes` flag", ); - Self::lock_non_cardinal_outputs(client, &inscriptions, &runic_outputs, unspent_outputs)?; + Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; let (id, entry) = index .rune(spaced_rune.rune)? @@ -251,7 +250,7 @@ impl Send { value: 0, }, TxOut { - script_pubkey: get_change_address(client, chain)?.script_pubkey(), + script_pubkey: wallet.get_change_address(chain)?.script_pubkey(), value: TARGET_POSTAGE.to_sat(), }, TxOut { @@ -261,12 +260,18 @@ impl Send { ], }; - let unsigned_transaction = fund_raw_transaction(client, fee_rate, &unfunded_transaction)?; + let unsigned_transaction = + fund_raw_transaction(&wallet.bitcoin_rpc_client, fee_rate, &unfunded_transaction)?; - let signed_transaction = client + let signed_transaction = wallet + .bitcoin_rpc_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - Ok(client.send_raw_transaction(&signed_transaction)?) + Ok( + wallet + .bitcoin_rpc_client + .send_raw_transaction(&signed_transaction)?, + ) } } diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index fc77370cba..041c76d754 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -13,8 +13,8 @@ pub struct Output { } impl Transactions { - pub(crate) fn run(self, wallet: String, options: Options) -> SubcommandResult { - let client = bitcoin_rpc_client_for_wallet(wallet, &options)?; + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + let client = wallet.bitcoin_rpc_client; let mut output = Vec::new(); for tx in client.list_transactions( From 548ddae6ddabc04d065ad80bf79807f54e69be30 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 2 Jan 2024 02:30:15 +0100 Subject: [PATCH 04/74] Place clippy + fmt --- src/subcommand/wallet.rs | 9 +++++---- src/subcommand/wallet/inscribe/batch.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 4eecf57ad7..24c69708f1 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -71,7 +71,7 @@ impl WalletCommand { let handle = axum_server::Handle::new(); LISTENERS.lock().unwrap().push(handle.clone()); - let ord_api_url: Url = format!("http://127.0.0.1:8080").parse().unwrap(); + let ord_api_url: Url = "http://127.0.0.1:8080".parse().unwrap(); { let options = options.clone(); @@ -237,9 +237,10 @@ impl Wallet { let network = options.chain().network(); - let _ = &self - .bitcoin_rpc_client - .create_wallet(&self.wallet_name, None, Some(true), None, None)?; + let _ = + &self + .bitcoin_rpc_client + .create_wallet(&self.wallet_name, None, Some(true), None, None)?; let secp = Secp256k1::new(); diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index 08fb8042fd..b60aceff14 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -102,7 +102,7 @@ impl Batch { }; if !self.no_backup { - Self::backup_recovery_key(&wallet, recovery_key_pair, chain.network())?; + Self::backup_recovery_key(wallet, recovery_key_pair, chain.network())?; } let commit = wallet diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 201dc28033..0f0bd475aa 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -179,7 +179,7 @@ impl Send { "sending runes with `ord send` requires index created with `--index-runes` flag", ); - Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; + Self::lock_non_cardinal_outputs(wallet, &inscriptions, &runic_outputs, unspent_outputs)?; let (id, entry) = index .rune(spaced_rune.rune)? From bb9ab59e7626254d01776ccb6bca42c90ebcd19a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 3 Jan 2024 20:45:10 +0100 Subject: [PATCH 05/74] Put chain in wallet --- src/subcommand/wallet.rs | 8 +++++--- src/subcommand/wallet/etch.rs | 2 +- src/subcommand/wallet/inscribe.rs | 11 +++++------ src/subcommand/wallet/inscribe/batch.rs | 9 +++------ src/subcommand/wallet/send.rs | 11 ++--------- 5 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 24c69708f1..38e49cee0f 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -98,6 +98,7 @@ impl WalletCommand { let wallet = Wallet { bitcoin_rpc_client: bitcoin_rpc_client_for_wallet(self.name.clone(), &options)?, + chain: options.chain(), ord_api_url, ord_http_client: { let mut headers = header::HeaderMap::new(); @@ -131,6 +132,7 @@ impl WalletCommand { pub(crate) struct Wallet { pub(crate) bitcoin_rpc_client: Client, + pub(crate) chain: Chain, pub(crate) ord_api_url: Url, pub(crate) ord_http_client: reqwest::blocking::Client, // TODO: make async instead of blocking pub(crate) wallet_name: String, @@ -222,20 +224,20 @@ impl Wallet { ) } - pub(crate) fn get_change_address(&self, chain: Chain) -> Result
{ + pub(crate) fn get_change_address(&self) -> Result
{ Ok( self .bitcoin_rpc_client .call::>("getrawchangeaddress", &["bech32m".into()]) .context("could not get change addresses from wallet")? - .require_network(chain.network())?, + .require_network(self.chain.network())?, ) } pub(crate) fn initialize(&self, options: &Options, seed: [u8; 64]) -> Result { let _client = check_version(options.bitcoin_rpc_client(None)?)?; - let network = options.chain().network(); + let network = self.chain.network(); let _ = &self diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index efa47e6d3e..0ec249afb9 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -56,7 +56,7 @@ impl Etch { " must be equal to or less than 38" ); - let destination = wallet.get_change_address(options.chain())?; + let destination = wallet.get_change_address()?; let runestone = Runestone { etching: Some(Etching { diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index ffca2e9da8..0feee2f4a3 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -119,7 +119,7 @@ impl Inscribe { let runic_utxos = index.get_runic_outputs(&utxos.keys().cloned().collect::>())?; - let chain = options.chain(); + let chain = wallet.chain; let postage; let destinations; @@ -130,7 +130,7 @@ impl Inscribe { match (self.file, self.batch) { (Some(file), None) => { - parent_info = Inscribe::get_parent_info(self.parent, &index, &utxos, chain, &wallet)?; + parent_info = Inscribe::get_parent_info(self.parent, &index, &utxos, &wallet)?; postage = self.postage.unwrap_or(TARGET_POSTAGE); @@ -150,13 +150,13 @@ impl Inscribe { destinations = vec![match self.destination.clone() { Some(destination) => destination.require_network(chain.network())?, - None => wallet.get_change_address(chain)?, + None => wallet.get_change_address()?, }]; } (None, Some(batch)) => { let batchfile = Batchfile::load(&batch)?; - parent_info = Inscribe::get_parent_info(batchfile.parent, &index, &utxos, chain, &wallet)?; + parent_info = Inscribe::get_parent_info(batchfile.parent, &index, &utxos, &wallet)?; postage = batchfile .postage @@ -237,7 +237,6 @@ impl Inscribe { parent: Option, index: &Index, utxos: &BTreeMap, - chain: Chain, wallet: &Wallet, ) -> Result> { if let Some(parent_id) = parent { @@ -247,7 +246,7 @@ impl Inscribe { } Ok(Some(ParentInfo { - destination: wallet.get_change_address(chain)?, + destination: wallet.get_change_address()?, id: parent_id, location: satpoint, tx_out: index diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index b60aceff14..85b22a6755 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -46,10 +46,7 @@ impl Batch { ) -> SubcommandResult { let wallet_inscriptions = index.get_inscriptions(utxos)?; - let commit_tx_change = [ - wallet.get_change_address(chain)?, - wallet.get_change_address(chain)?, - ]; + let commit_tx_change = [wallet.get_change_address()?, wallet.get_change_address()?]; let (commit_tx, reveal_tx, recovery_key_pair, total_fees) = self .create_batch_inscription_transactions( @@ -644,13 +641,13 @@ impl Batchfile { } let destinations = match self.mode { - Mode::SharedOutput | Mode::SameSat => vec![wallet.get_change_address(chain)?], + Mode::SharedOutput | Mode::SameSat => vec![wallet.get_change_address()?], Mode::SeparateOutputs => self .inscriptions .iter() .map(|entry| { entry.destination.as_ref().map_or_else( - || wallet.get_change_address(chain), + || wallet.get_change_address(), |address| { address .clone() diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 0f0bd475aa..5408d06b8d 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -29,8 +29,6 @@ impl Send { index.update()?; - let chain = options.chain(); - let unspent_outputs = wallet.get_unspent_outputs()?; let locked_outputs = wallet.get_locked_outputs()?; @@ -52,7 +50,6 @@ impl Send { Outgoing::Rune { decimal, rune } => { let transaction = Self::send_runes( address, - chain, decimal, self.fee_rate, &index, @@ -80,10 +77,7 @@ impl Send { } }; - let change = [ - wallet.get_change_address(chain)?, - wallet.get_change_address(chain)?, - ]; + let change = [wallet.get_change_address()?, wallet.get_change_address()?]; let postage = if let Some(postage) = self.postage { Target::ExactPostage(postage) @@ -164,7 +158,6 @@ impl Send { fn send_runes( address: Address, - chain: Chain, decimal: Decimal, fee_rate: FeeRate, index: &Index, @@ -250,7 +243,7 @@ impl Send { value: 0, }, TxOut { - script_pubkey: wallet.get_change_address(chain)?.script_pubkey(), + script_pubkey: wallet.get_change_address()?.script_pubkey(), value: TARGET_POSTAGE.to_sat(), }, TxOut { From b3dcb8bd5ac20f97d91e22091854bf0a62281d24 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 3 Jan 2024 20:54:21 +0100 Subject: [PATCH 06/74] Fix wallet bug --- src/subcommand/wallet.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 38e49cee0f..03eff907cc 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -235,15 +235,16 @@ impl Wallet { } pub(crate) fn initialize(&self, options: &Options, seed: [u8; 64]) -> Result { - let _client = check_version(options.bitcoin_rpc_client(None)?)?; + check_version(options.bitcoin_rpc_client(None)?)?.create_wallet( + &self.wallet_name, + None, + Some(true), + None, + None, + )?; let network = self.chain.network(); - let _ = - &self - .bitcoin_rpc_client - .create_wallet(&self.wallet_name, None, Some(true), None, None)?; - let secp = Secp256k1::new(); let master_private_key = ExtendedPrivKey::new_master(network, &seed)?; @@ -261,6 +262,7 @@ impl Wallet { for change in [false, true] { self.derive_and_import_descriptor( + options, &secp, (fingerprint, derivation_path.clone()), derived_private_key, @@ -273,6 +275,7 @@ impl Wallet { fn derive_and_import_descriptor( &self, + options: &Options, secp: &Secp256k1, origin: (Fingerprint, DerivationPath), derived_private_key: ExtendedPrivKey, @@ -294,8 +297,8 @@ impl Wallet { let desc = Descriptor::new_tr(public_key, None)?; - self - .bitcoin_rpc_client + options + .bitcoin_rpc_client(Some(self.wallet_name.clone()))? .import_descriptors(ImportDescriptors { descriptor: desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, From cfd83c064af571cbda167b70d13c2f4a3d104b2c Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 3 Jan 2024 22:26:19 +0100 Subject: [PATCH 07/74] Getting somewhere; need to add status json endpoint --- src/index.rs | 2 +- src/lib.rs | 1 + src/subcommand/wallet.rs | 44 +++++++++++++++++++++++++ src/subcommand/wallet/inscribe.rs | 44 ++++++++++++------------- src/subcommand/wallet/inscribe/batch.rs | 8 ++--- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/index.rs b/src/index.rs index ed53b132ce..242ff997df 100644 --- a/src/index.rs +++ b/src/index.rs @@ -164,7 +164,7 @@ pub(crate) struct InscriptionInfo { pub(crate) charms: u16, } -trait BitcoinCoreRpcResultExt { +pub(crate) trait BitcoinCoreRpcResultExt { fn into_option(self) -> Result>; } diff --git a/src/lib.rs b/src/lib.rs index 3805416a4e..e48c90e5a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ use { }, sysinfo::System, tempfile::TempDir, + templates::{InscriptionJson, OutputJson}, tokio::{runtime::Runtime, task}, }; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 03eff907cc..c541411bd1 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -97,6 +97,7 @@ impl WalletCommand { } let wallet = Wallet { + // TODO: this won't work for create and restore bitcoin_rpc_client: bitcoin_rpc_client_for_wallet(self.name.clone(), &options)?, chain: options.chain(), ord_api_url, @@ -207,6 +208,49 @@ impl Wallet { .collect() } + pub(crate) fn get_inscription(&self, inscription_id: InscriptionId) -> Result { + let inscription_json: InscriptionJson = serde_json::from_str( + &self + .ord_http_client + .get( + self + .ord_api_url + .join(&format!("/inscription/{inscription_id}")) + .unwrap(), + ) + .send()? + .text()?, + )?; + + Ok(inscription_json) + } + + pub(crate) fn get_inscriptions( + &self, + utxos: &BTreeMap, + ) -> Result> { + let mut inscriptions = BTreeMap::new(); + for output in utxos.keys() { + let output_json: OutputJson = serde_json::from_str( + &self + .ord_http_client + .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) + .send()? + .text()?, + )?; + + for inscription in output_json.inscriptions { + inscriptions.insert(self.get_inscription_satpoint(inscription)?, inscription); + } + } + + Ok(inscriptions) + } + + pub(crate) fn get_inscription_satpoint(&self, inscription_id: InscriptionId) -> Result { + Ok(self.get_inscription(inscription_id)?.satpoint) + } + pub(crate) fn get_locked_outputs(&self) -> Result> { #[derive(Deserialize)] pub(crate) struct JsonOutPoint { diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 0feee2f4a3..af87504069 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -130,7 +130,7 @@ impl Inscribe { match (self.file, self.batch) { (Some(file), None) => { - parent_info = Inscribe::get_parent_info(self.parent, &index, &utxos, &wallet)?; + parent_info = Inscribe::get_parent_info(self.parent, &utxos, &wallet)?; postage = self.postage.unwrap_or(TARGET_POSTAGE); @@ -156,7 +156,7 @@ impl Inscribe { (None, Some(batch)) => { let batchfile = Batchfile::load(&batch)?; - parent_info = Inscribe::get_parent_info(batchfile.parent, &index, &utxos, &wallet)?; + parent_info = Inscribe::get_parent_info(batchfile.parent, &utxos, &wallet)?; postage = batchfile .postage @@ -211,7 +211,7 @@ impl Inscribe { reveal_fee_rate: self.fee_rate, satpoint, } - .inscribe(chain, &index, &locked_utxos, runic_utxos, &utxos, &wallet) + .inscribe(&locked_utxos, runic_utxos, &utxos, &wallet) } fn parse_metadata(cbor: Option, json: Option) -> Result>> { @@ -235,31 +235,31 @@ impl Inscribe { fn get_parent_info( parent: Option, - index: &Index, utxos: &BTreeMap, wallet: &Wallet, ) -> Result> { if let Some(parent_id) = parent { - if let Some(satpoint) = index.get_inscription_satpoint_by_id(parent_id)? { - if !utxos.contains_key(&satpoint.outpoint) { - return Err(anyhow!(format!("parent {parent_id} not in wallet"))); - } + let satpoint = wallet + .get_inscription_satpoint(parent_id) + .map_err(|_| anyhow!(format!("parent {parent_id} does not exist")))?; - Ok(Some(ParentInfo { - destination: wallet.get_change_address()?, - id: parent_id, - location: satpoint, - tx_out: index - .get_transaction(satpoint.outpoint.txid)? - .expect("parent transaction not found in index") - .output - .into_iter() - .nth(satpoint.outpoint.vout.try_into().unwrap()) - .expect("current transaction output"), - })) - } else { - Err(anyhow!(format!("parent {parent_id} does not exist"))) + if !utxos.contains_key(&satpoint.outpoint) { + return Err(anyhow!(format!("parent {parent_id} not in wallet"))); } + + Ok(Some(ParentInfo { + destination: wallet.get_change_address()?, + id: parent_id, + location: satpoint, + tx_out: wallet + .bitcoin_rpc_client + .get_raw_transaction(&satpoint.outpoint.txid, None) + .expect("parent transaction not found in index") + .output + .into_iter() + .nth(satpoint.outpoint.vout.try_into().unwrap()) + .expect("current transaction output"), + })) } else { Ok(None) } diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index 85b22a6755..016a884260 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -37,21 +37,19 @@ impl Default for Batch { impl Batch { pub(crate) fn inscribe( &self, - chain: Chain, - index: &Index, locked_utxos: &BTreeSet, runic_utxos: BTreeSet, utxos: &BTreeMap, wallet: &Wallet, ) -> SubcommandResult { - let wallet_inscriptions = index.get_inscriptions(utxos)?; + let wallet_inscriptions = wallet.get_inscriptions(utxos)?; let commit_tx_change = [wallet.get_change_address()?, wallet.get_change_address()?]; let (commit_tx, reveal_tx, recovery_key_pair, total_fees) = self .create_batch_inscription_transactions( wallet_inscriptions, - chain, + wallet.chain, locked_utxos.clone(), runic_utxos, utxos.clone(), @@ -99,7 +97,7 @@ impl Batch { }; if !self.no_backup { - Self::backup_recovery_key(wallet, recovery_key_pair, chain.network())?; + Self::backup_recovery_key(wallet, recovery_key_pair, wallet.chain.network())?; } let commit = wallet From 00d72a4bd800da5d8f0ee30af4aa06888087f32d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 3 Jan 2024 22:34:03 +0100 Subject: [PATCH 08/74] Add TODO --- src/subcommand/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index c541411bd1..9a5227212b 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -132,7 +132,7 @@ impl WalletCommand { } pub(crate) struct Wallet { - pub(crate) bitcoin_rpc_client: Client, + pub(crate) bitcoin_rpc_client: Client, // TODO: make this a function instead pub(crate) chain: Chain, pub(crate) ord_api_url: Url, pub(crate) ord_http_client: reqwest::blocking::Client, // TODO: make async instead of blocking From c495f1ffe42b465d73707c2818aa68e9fbec68d7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 00:56:41 +0100 Subject: [PATCH 09/74] Get server status --- src/index.rs | 1 + src/lib.rs | 2 +- src/subcommand/wallet.rs | 12 ++++++++++++ src/subcommand/wallet/inscribe.rs | 2 +- src/templates.rs | 2 +- src/templates/status.rs | 2 ++ tests/json_api.rs | 4 ++-- tests/lib.rs | 2 +- 8 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/index.rs b/src/index.rs index 232ee7186f..42287115ab 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3403,6 +3403,7 @@ mod tests { &context.options, ) .unwrap(), + chain: context.options.chain(), ord_api_url: "127.0.0.1:8080".parse().unwrap(), ord_http_client: reqwest::blocking::Client::new(), wallet_name: "ord".into(), diff --git a/src/lib.rs b/src/lib.rs index 0e64427d3e..f6b39b5d55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ use { }, sysinfo::System, tempfile::TempDir, - templates::{InscriptionJson, OutputJson}, + templates::{InscriptionJson, OutputJson, StatusJson}, tokio::{runtime::Runtime, task}, }; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 9a5227212b..d07b570576 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -251,6 +251,18 @@ impl Wallet { Ok(self.get_inscription(inscription_id)?.satpoint) } + pub(crate) fn get_server_status(&self) -> Result { + let status: StatusJson = serde_json::from_str( + &self + .ord_http_client + .get(self.ord_api_url.join(&format!("/status")).unwrap()) + .send()? + .text()?, + )?; + + Ok(status) + } + pub(crate) fn get_locked_outputs(&self) -> Result> { #[derive(Deserialize)] pub(crate) struct JsonOutPoint { diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index af87504069..0f19282165 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -184,7 +184,7 @@ impl Inscribe { } let satpoint = if let Some(sat) = sat { - if !index.has_sat_index() { + if !wallet.get_server_status()?.sat_index { return Err(anyhow!( "index must be built with `--index-sats` to use `--sat`" )); diff --git a/src/templates.rs b/src/templates.rs index 4a53f94836..4c2aab88e0 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -24,7 +24,7 @@ pub(crate) use { runes::RunesHtml, sat::{SatHtml, SatInscriptionJson, SatInscriptionsJson, SatJson}, server_config::ServerConfig, - status::StatusHtml, + status::{StatusHtml, StatusJson}, transaction::TransactionHtml, }; diff --git a/src/templates/status.rs b/src/templates/status.rs index 2181cf2261..78a2046c25 100644 --- a/src/templates/status.rs +++ b/src/templates/status.rs @@ -1,5 +1,7 @@ use super::*; +pub type StatusJson = StatusHtml; + #[derive(Boilerplate, Debug, PartialEq, Serialize, Deserialize)] pub struct StatusHtml { pub blessed_inscriptions: u64, diff --git a/tests/json_api.rs b/tests/json_api.rs index 2be51014f4..fc6898dfcc 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -380,7 +380,7 @@ fn get_status() { assert_eq!(response.status(), StatusCode::OK); - let mut status_json: StatusHtml = serde_json::from_str(&response.text().unwrap()).unwrap(); + let mut status_json: StatusJson = serde_json::from_str(&response.text().unwrap()).unwrap(); let dummy_started = "2012-12-12 12:12:12+00:00" .parse::>() @@ -393,7 +393,7 @@ fn get_status() { pretty_assert_eq!( status_json, - StatusHtml { + StatusJson { blessed_inscriptions: 1, cursed_inscriptions: 0, chain: Chain::Mainnet, diff --git a/tests/lib.rs b/tests/lib.rs index 0942ad1acf..4533bd8db8 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -15,7 +15,7 @@ use { subcommand::runes::RuneInfo, templates::{ block::BlockJson, inscription::InscriptionJson, inscriptions::InscriptionsJson, - output::OutputJson, sat::SatJson, status::StatusHtml, + output::OutputJson, sat::SatJson, status::StatusJson, }, Edict, InscriptionId, Rune, RuneId, Runestone, SatPoint, }, From ab9b08006d9d10a7ba746e0e2b8e4b42877b9130 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 16:01:13 +0100 Subject: [PATCH 10/74] Inscribe does not use index --- src/subcommand/wallet.rs | 65 ++++++++++++++++++++++++++++++- src/subcommand/wallet/inscribe.rs | 17 ++------ 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index d07b570576..702096ecce 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -118,7 +118,7 @@ impl WalletCommand { Subcommand::Balance => balance::run(wallet, options), Subcommand::Create(create) => create.run(wallet, options), Subcommand::Etch(etch) => etch.run(wallet, options), - Subcommand::Inscribe(inscribe) => inscribe.run(wallet, options), + Subcommand::Inscribe(inscribe) => inscribe.run(wallet), Subcommand::Inscriptions => inscriptions::run(wallet, options), Subcommand::Receive => receive::run(wallet), Subcommand::Restore(restore) => restore.run(wallet, options), @@ -251,6 +251,69 @@ impl Wallet { Ok(self.get_inscription(inscription_id)?.satpoint) } + pub(crate) fn find_sat_in_outputs( + &self, + sat: Sat, + utxos: &BTreeMap, + ) -> Result { + if !self.get_server_status()?.sat_index { + return Err(anyhow!( + "index must be built with `--index-sats` to use `--sat`" + )); + } + + for output in utxos.keys() { + let output_json: OutputJson = serde_json::from_str( + &self + .ord_http_client + .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) + .send()? + .text()?, + )?; + + if let Some(sat_ranges) = output_json.sat_ranges { + let mut offset = 0; + for (start, end) in sat_ranges { + if start <= sat.n() && sat.n() < end { + return Ok(SatPoint { + outpoint: *output, + offset: offset + sat.n() - start, + }); + } + offset += end - start; + } + } else { + continue; + } + } + + Err(anyhow!(format!( + "could not find sat `{sat}` in wallet outputs" + ))) + } + + pub(crate) fn get_runic_outputs( + &self, + utxos: &BTreeMap, + ) -> Result> { + let mut runic_outputs = BTreeSet::new(); + for output in utxos.keys() { + let output_json: OutputJson = serde_json::from_str( + &self + .ord_http_client + .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) + .send()? + .text()?, + )?; + + if !output_json.runes.is_empty() { + runic_outputs.insert(*output); + } + } + + Ok(runic_outputs) + } + pub(crate) fn get_server_status(&self) -> Result { let status: StatusJson = serde_json::from_str( &self diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 0f19282165..ac8367c9c9 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -107,17 +107,14 @@ pub(crate) struct Inscribe { } impl Inscribe { - pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { let metadata = Inscribe::parse_metadata(self.cbor_metadata, self.json_metadata)?; - let index = Index::open(&options)?; - index.update()?; - let utxos = wallet.get_unspent_outputs()?; let locked_utxos = wallet.get_locked_outputs()?; - let runic_utxos = index.get_runic_outputs(&utxos.keys().cloned().collect::>())?; + let runic_utxos = wallet.get_runic_outputs(&utxos)?; let chain = wallet.chain; @@ -184,15 +181,7 @@ impl Inscribe { } let satpoint = if let Some(sat) = sat { - if !wallet.get_server_status()?.sat_index { - return Err(anyhow!( - "index must be built with `--index-sats` to use `--sat`" - )); - } - match index.find(sat)? { - Some(satpoint) => Some(satpoint), - None => return Err(anyhow!(format!("could not find sat `{sat}`"))), - } + Some(wallet.find_sat_in_outputs(sat, &utxos)?) } else { self.satpoint }; From b0771dd33a2d0454f9872a3e8fdf685138252dca Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 16:35:46 +0100 Subject: [PATCH 11/74] Rid balance command of the index --- src/runes/pile.rs | 9 +++++---- src/subcommand/server.rs | 5 +---- src/subcommand/wallet.rs | 30 +++++++++++++++++++++++++++++- src/subcommand/wallet/balance.rs | 13 +++++-------- src/templates/output.rs | 4 ++-- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/runes/pile.rs b/src/runes/pile.rs index 4ba6822514..8ab1a4e4ee 100644 --- a/src/runes/pile.rs +++ b/src/runes/pile.rs @@ -1,9 +1,10 @@ use super::*; -pub(crate) struct Pile { - pub(crate) amount: u128, - pub(crate) divisibility: u8, - pub(crate) symbol: Option, +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct Pile { + pub amount: u128, + pub divisibility: u8, + pub symbol: Option, } impl Display for Pile { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index db27e8cbaf..f1fb0913f8 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -573,10 +573,7 @@ impl Server { server_config.chain, output, inscriptions, - runes - .into_iter() - .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) - .collect(), + runes, )) .into_response() } else { diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 702096ecce..20406e9429 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -115,7 +115,7 @@ impl WalletCommand { }; match self.subcommand { - Subcommand::Balance => balance::run(wallet, options), + Subcommand::Balance => balance::run(wallet), Subcommand::Create(create) => create.run(wallet, options), Subcommand::Etch(etch) => etch.run(wallet, options), Subcommand::Inscribe(inscribe) => inscribe.run(wallet), @@ -314,6 +314,34 @@ impl Wallet { Ok(runic_outputs) } + pub(crate) fn get_rune_balances_for_outpoint( + &self, + output: &OutPoint, + ) -> Result> { + Ok( + serde_json::from_str::( + &self + .ord_http_client + .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) + .send()? + .text()?, + )? + .runes, + ) + } + + pub(crate) fn get_rune_balances( + &self, + utxos: &BTreeMap, + ) -> Result> { + let mut rune_balances = Vec::new(); + for output in utxos.keys() { + rune_balances.append(&mut self.get_rune_balances_for_outpoint(output)?); + } + + Ok(rune_balances) + } + pub(crate) fn get_server_status(&self) -> Result { let status: StatusJson = serde_json::from_str( &self diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index a4ca9092e5..1a4de5c9c7 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -11,13 +11,10 @@ pub struct Output { pub total: u64, } -pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - index.update()?; - +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let unspent_outputs = wallet.get_unspent_outputs()?; - let inscription_outputs = index + let inscription_outputs = wallet .get_inscriptions(&unspent_outputs)? .keys() .map(|satpoint| satpoint.outpoint) @@ -29,7 +26,7 @@ pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { let mut runic = 0; for (outpoint, amount) in unspent_outputs { - let rune_balances = index.get_rune_balances_for_outpoint(outpoint)?; + let rune_balances = wallet.get_rune_balances_for_outpoint(&outpoint)?; if inscription_outputs.contains(&outpoint) { ordinal += amount.to_sat(); @@ -46,8 +43,8 @@ pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { Ok(Box::new(Output { cardinal, ordinal, - runes: index.has_rune_index().then_some(runes), - runic: index.has_rune_index().then_some(runic), + runes: wallet.get_server_status()?.rune_index.then_some(runes), + runic: wallet.get_server_status()?.rune_index.then_some(runic), total: cardinal + ordinal + runic, })) } diff --git a/src/templates/output.rs b/src/templates/output.rs index 08064e073a..9c05720132 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -18,7 +18,7 @@ pub struct OutputJson { pub transaction: String, pub sat_ranges: Option>, pub inscriptions: Vec, - pub runes: BTreeMap, + pub runes: Vec<(SpacedRune, Pile)>, } impl OutputJson { @@ -28,7 +28,7 @@ impl OutputJson { chain: Chain, output: TxOut, inscriptions: Vec, - runes: BTreeMap, + runes: Vec<(SpacedRune, Pile)>, ) -> Self { Self { value: output.value, From 44c350e720e4ed3d787c27b12199f7d75e54b1ec Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 17:25:51 +0100 Subject: [PATCH 12/74] inscriptions command no index --- src/subcommand/wallet/etch.rs | 7 +++---- src/subcommand/wallet/inscriptions.rs | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 0ec249afb9..9fb531691b 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -21,13 +21,12 @@ pub struct Output { impl Etch { pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - ensure!( - index.has_rune_index(), + wallet.get_server_status()?.rune_index, "`ord wallet etch` requires index created with `--index-runes` flag", ); + let index = Index::open(&options)?; index.update()?; let SpacedRune { rune, spacers } = self.rune; @@ -103,7 +102,7 @@ impl Etch { let unspent_outputs = wallet.get_unspent_outputs()?; - let inscriptions = index + let inscriptions = wallet .get_inscriptions(&unspent_outputs)? .keys() .map(|satpoint| satpoint.outpoint) diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 1c08021c94..6b0ed67a50 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -9,12 +9,9 @@ pub struct Output { } pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - index.update()?; - let unspent_outputs = wallet.get_unspent_outputs()?; - let inscriptions = index.get_inscriptions(&unspent_outputs)?; + let inscriptions = wallet.get_inscriptions(&unspent_outputs)?; let explorer = match options.chain() { Chain::Mainnet => "https://ordinals.com/inscription/", From c8ea7864bb3eba8c5adfe8cbef9ccd5ab0a8850f Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 18:21:21 +0100 Subject: [PATCH 13/74] Rid sats command of index --- src/subcommand/wallet.rs | 170 +++++++++++++------------------ src/subcommand/wallet/balance.rs | 6 +- src/subcommand/wallet/sats.rs | 10 +- 3 files changed, 75 insertions(+), 111 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 20406e9429..1c9d5f6b41 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -122,7 +122,7 @@ impl WalletCommand { Subcommand::Inscriptions => inscriptions::run(wallet, options), Subcommand::Receive => receive::run(wallet), Subcommand::Restore(restore) => restore.run(wallet, options), - Subcommand::Sats(sats) => sats.run(wallet, options), + Subcommand::Sats(sats) => sats.run(wallet), Subcommand::Send(send) => send.run(wallet, options), Subcommand::Transactions(transactions) => transactions.run(wallet), Subcommand::Outputs => outputs::run(wallet, options), @@ -170,85 +170,30 @@ impl Wallet { ); } - for outpoint in utxos.keys() { - if self - .ord_http_client - .get( - self - .ord_api_url - .join(&format!("/output/{outpoint}")) - .unwrap(), - ) - .send() - .unwrap() - .status() - .is_client_error() - { - return Err(anyhow!( - "output in Bitcoin Core wallet but not in ord index: {outpoint}" - )); - } + for output in utxos.keys() { + self.get_output(output)?; } Ok(utxos) } - pub(crate) fn get_unspent_output_ranges( - &self, - index: &Index, - ) -> Result)>> { - self - .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() - } - - pub(crate) fn get_inscription(&self, inscription_id: InscriptionId) -> Result { - let inscription_json: InscriptionJson = serde_json::from_str( - &self - .ord_http_client - .get( - self - .ord_api_url - .join(&format!("/inscription/{inscription_id}")) - .unwrap(), - ) - .send()? - .text()?, - )?; - - Ok(inscription_json) - } - - pub(crate) fn get_inscriptions( - &self, - utxos: &BTreeMap, - ) -> Result> { - let mut inscriptions = BTreeMap::new(); - for output in utxos.keys() { - let output_json: OutputJson = serde_json::from_str( - &self - .ord_http_client - .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) - .send()? - .text()?, - )?; + pub(crate) fn get_output_sat_ranges(&self) -> Result)>> { + if !self.get_server_status()?.sat_index { + return Err(anyhow!( + "index must be built with `--index-sats` to use `--sat`" + )); + } - for inscription in output_json.inscriptions { - inscriptions.insert(self.get_inscription_satpoint(inscription)?, inscription); + let mut output_sat_ranges = Vec::new(); + for output in self.get_unspent_outputs()?.keys() { + if let Some(sat_ranges) = self.get_output(output)?.sat_ranges { + output_sat_ranges.push((*output, sat_ranges)); + } else { + bail!("output {output} in wallet but is spent according to index"); } } - Ok(inscriptions) - } - - pub(crate) fn get_inscription_satpoint(&self, inscription_id: InscriptionId) -> Result { - Ok(self.get_inscription(inscription_id)?.satpoint) + Ok(output_sat_ranges) } pub(crate) fn find_sat_in_outputs( @@ -263,15 +208,7 @@ impl Wallet { } for output in utxos.keys() { - let output_json: OutputJson = serde_json::from_str( - &self - .ord_http_client - .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) - .send()? - .text()?, - )?; - - if let Some(sat_ranges) = output_json.sat_ranges { + if let Some(sat_ranges) = self.get_output(&output)?.sat_ranges { let mut offset = 0; for (start, end) in sat_ranges { if start <= sat.n() && sat.n() < end { @@ -292,21 +229,61 @@ impl Wallet { ))) } + fn get_output(&self, output: &OutPoint) -> Result { + let response = self + .ord_http_client + .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) + .send()?; + + if response.status().is_client_error() { + bail!("output in Bitcoin Core wallet but not in ord index: {output}"); + } + + Ok(serde_json::from_str(&response.text()?)?) + } + + pub(crate) fn get_inscription(&self, inscription_id: InscriptionId) -> Result { + let inscription_json: InscriptionJson = serde_json::from_str( + &self + .ord_http_client + .get( + self + .ord_api_url + .join(&format!("/inscription/{inscription_id}")) + .unwrap(), + ) + .send()? + .text()?, + )?; + + Ok(inscription_json) + } + + pub(crate) fn get_inscriptions( + &self, + utxos: &BTreeMap, + ) -> Result> { + let mut inscriptions = BTreeMap::new(); + for output in utxos.keys() { + for inscription in self.get_output(&output)?.inscriptions { + inscriptions.insert(self.get_inscription_satpoint(inscription)?, inscription); + } + } + + Ok(inscriptions) + } + + pub(crate) fn get_inscription_satpoint(&self, inscription_id: InscriptionId) -> Result { + Ok(self.get_inscription(inscription_id)?.satpoint) + } + pub(crate) fn get_runic_outputs( &self, utxos: &BTreeMap, ) -> Result> { let mut runic_outputs = BTreeSet::new(); for output in utxos.keys() { - let output_json: OutputJson = serde_json::from_str( - &self - .ord_http_client - .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) - .send()? - .text()?, - )?; - - if !output_json.runes.is_empty() { + if !self.get_output(output)?.runes.is_empty() { runic_outputs.insert(*output); } } @@ -314,20 +291,11 @@ impl Wallet { Ok(runic_outputs) } - pub(crate) fn get_rune_balances_for_outpoint( + pub(crate) fn get_rune_balances_for_outputs( &self, output: &OutPoint, ) -> Result> { - Ok( - serde_json::from_str::( - &self - .ord_http_client - .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) - .send()? - .text()?, - )? - .runes, - ) + Ok(self.get_output(output)?.runes) } pub(crate) fn get_rune_balances( @@ -336,7 +304,7 @@ impl Wallet { ) -> Result> { let mut rune_balances = Vec::new(); for output in utxos.keys() { - rune_balances.append(&mut self.get_rune_balances_for_outpoint(output)?); + rune_balances.append(&mut self.get_rune_balances_for_outputs(output)?); } Ok(rune_balances) diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index 1a4de5c9c7..e6e7c4d4ac 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -25,10 +25,10 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let mut runes = BTreeMap::new(); let mut runic = 0; - for (outpoint, amount) in unspent_outputs { - let rune_balances = wallet.get_rune_balances_for_outpoint(&outpoint)?; + for (output, amount) in unspent_outputs { + let rune_balances = wallet.get_rune_balances_for_outputs(&output)?; - if inscription_outputs.contains(&outpoint) { + if inscription_outputs.contains(&output) { ordinal += amount.to_sat(); } else if !rune_balances.is_empty() { for (spaced_rune, pile) in rune_balances { diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 860176e927..b8e908935e 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -24,16 +24,12 @@ pub struct OutputRare { } impl Sats { - pub(crate) fn run(&self, wallet: Wallet, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - - if !index.has_sat_index() { + pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { + if !wallet.get_server_status()?.sat_index { bail!("sats requires index created with `--index-sats` flag"); } - index.update()?; - - let utxos = wallet.get_unspent_output_ranges(&index)?; + let utxos = wallet.get_output_sat_ranges()?; if let Some(path) = &self.tsv { let mut output = Vec::new(); From 903857d9fc8eb506b51b34a7567c0421dddf3a04 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 18:50:07 +0100 Subject: [PATCH 14/74] Start work on send, waiting for rune json endpoint --- src/subcommand/wallet.rs | 63 +++++++++++++++---------- src/subcommand/wallet/balance.rs | 4 +- src/subcommand/wallet/etch.rs | 4 +- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/inscribe/batch.rs | 2 +- src/subcommand/wallet/inscriptions.rs | 2 +- src/subcommand/wallet/send.rs | 13 ++--- 7 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 1c9d5f6b41..5f64202159 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -242,29 +242,27 @@ impl Wallet { Ok(serde_json::from_str(&response.text()?)?) } - pub(crate) fn get_inscription(&self, inscription_id: InscriptionId) -> Result { - let inscription_json: InscriptionJson = serde_json::from_str( - &self - .ord_http_client - .get( - self - .ord_api_url - .join(&format!("/inscription/{inscription_id}")) - .unwrap(), - ) - .send()? - .text()?, - )?; + fn get_inscription(&self, inscription_id: InscriptionId) -> Result { + let response = self + .ord_http_client + .get( + self + .ord_api_url + .join(&format!("/inscription/{inscription_id}")) + .unwrap(), + ) + .send()?; - Ok(inscription_json) + if response.status().is_client_error() { + bail!("inscription {inscription_id} not found"); + } + + Ok(serde_json::from_str(&response.text()?)?) } - pub(crate) fn get_inscriptions( - &self, - utxos: &BTreeMap, - ) -> Result> { + pub(crate) fn get_inscriptions(&self) -> Result> { let mut inscriptions = BTreeMap::new(); - for output in utxos.keys() { + for output in self.get_unspent_outputs()?.keys() { for inscription in self.get_output(&output)?.inscriptions { inscriptions.insert(self.get_inscription_satpoint(inscription)?, inscription); } @@ -277,12 +275,9 @@ impl Wallet { Ok(self.get_inscription(inscription_id)?.satpoint) } - pub(crate) fn get_runic_outputs( - &self, - utxos: &BTreeMap, - ) -> Result> { + pub(crate) fn get_runic_outputs(&self) -> Result> { let mut runic_outputs = BTreeSet::new(); - for output in utxos.keys() { + for output in self.get_unspent_outputs()?.keys() { if !self.get_output(output)?.runes.is_empty() { runic_outputs.insert(*output); } @@ -291,20 +286,36 @@ impl Wallet { Ok(runic_outputs) } - pub(crate) fn get_rune_balances_for_outputs( + pub(crate) fn get_runes_balances_for_output( &self, output: &OutPoint, ) -> Result> { Ok(self.get_output(output)?.runes) } + pub(crate) fn get_rune_balance_in_output(&self, output: &OutPoint, rune: Rune) -> Result { + Ok( + self + .get_runes_balances_for_output(output)? + .iter() + .map(|(spaced_rune, pile)| { + if spaced_rune.rune == rune { + pile.amount + } else { + 0 + } + }) + .sum(), + ) + } + pub(crate) fn get_rune_balances( &self, utxos: &BTreeMap, ) -> Result> { let mut rune_balances = Vec::new(); for output in utxos.keys() { - rune_balances.append(&mut self.get_rune_balances_for_outputs(output)?); + rune_balances.append(&mut self.get_runes_balances_for_output(output)?); } Ok(rune_balances) diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index e6e7c4d4ac..5c4bc84c26 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -15,7 +15,7 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let unspent_outputs = wallet.get_unspent_outputs()?; let inscription_outputs = wallet - .get_inscriptions(&unspent_outputs)? + .get_inscriptions()? .keys() .map(|satpoint| satpoint.outpoint) .collect::>(); @@ -26,7 +26,7 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let mut runic = 0; for (output, amount) in unspent_outputs { - let rune_balances = wallet.get_rune_balances_for_outputs(&output)?; + let rune_balances = wallet.get_runes_balances_for_output(&output)?; if inscription_outputs.contains(&output) { ordinal += amount.to_sat(); diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 9fb531691b..4e27ffcc8a 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -100,10 +100,8 @@ impl Etch { ], }; - let unspent_outputs = wallet.get_unspent_outputs()?; - let inscriptions = wallet - .get_inscriptions(&unspent_outputs)? + .get_inscriptions()? .keys() .map(|satpoint| satpoint.outpoint) .collect::>(); diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index ac8367c9c9..da8da6335f 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -114,7 +114,7 @@ impl Inscribe { let locked_utxos = wallet.get_locked_outputs()?; - let runic_utxos = wallet.get_runic_outputs(&utxos)?; + let runic_utxos = wallet.get_runic_outputs()?; let chain = wallet.chain; diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index 016a884260..7a70262f5e 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -42,7 +42,7 @@ impl Batch { utxos: &BTreeMap, wallet: &Wallet, ) -> SubcommandResult { - let wallet_inscriptions = wallet.get_inscriptions(utxos)?; + let wallet_inscriptions = wallet.get_inscriptions()?; let commit_tx_change = [wallet.get_change_address()?, wallet.get_change_address()?]; diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 6b0ed67a50..36cd5c8871 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -11,7 +11,7 @@ pub struct Output { pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { let unspent_outputs = wallet.get_unspent_outputs()?; - let inscriptions = wallet.get_inscriptions(&unspent_outputs)?; + let inscriptions = wallet.get_inscriptions()?; let explorer = match options.chain() { Chain::Mainnet => "https://ordinals.com/inscription/", diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 5408d06b8d..8c2ef2dd02 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -33,10 +33,9 @@ impl Send { let locked_outputs = wallet.get_locked_outputs()?; - let inscriptions = index.get_inscriptions(&unspent_outputs)?; + let inscriptions = wallet.get_inscriptions()?; - let runic_outputs = - index.get_runic_outputs(&unspent_outputs.keys().cloned().collect::>())?; + let runic_outputs = wallet.get_runic_outputs()?; let satpoint = match self.outgoing { Outgoing::Amount(amount) => { @@ -44,9 +43,7 @@ impl Send { let transaction = Self::send_amount(&wallet, amount, address, self.fee_rate)?; return Ok(Box::new(Output { transaction })); } - Outgoing::InscriptionId(id) => index - .get_inscription_satpoint_by_id(id)? - .ok_or_else(|| anyhow!("inscription {id} not found"))?, + Outgoing::InscriptionId(id) => wallet.get_inscription_satpoint(id)?, Outgoing::Rune { decimal, rune } => { let transaction = Self::send_runes( address, @@ -168,7 +165,7 @@ impl Send { wallet: &Wallet, ) -> Result { ensure!( - index.has_rune_index(), + wallet.get_server_status()?.rune_index, "sending runes with `ord send` requires index created with `--index-runes` flag", ); @@ -193,7 +190,7 @@ impl Send { continue; } - let balance = index.get_rune_balance(output, id)?; + let balance = wallet.get_rune_balance_in_output(&output, entry.rune)?; if balance > 0 { input_runes += balance; From 69d4ba220b28b882451f0a77bfcbb9b2ee4085f2 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 4 Jan 2024 18:52:25 +0100 Subject: [PATCH 15/74] output and cardinal command no index --- src/subcommand/wallet.rs | 4 ++-- src/subcommand/wallet/cardinals.rs | 10 +++------- src/subcommand/wallet/outputs.rs | 6 +----- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 5f64202159..5c8840bddd 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -125,8 +125,8 @@ impl WalletCommand { Subcommand::Sats(sats) => sats.run(wallet), Subcommand::Send(send) => send.run(wallet, options), Subcommand::Transactions(transactions) => transactions.run(wallet), - Subcommand::Outputs => outputs::run(wallet, options), - Subcommand::Cardinals => cardinals::run(wallet, options), + Subcommand::Outputs => outputs::run(wallet), + Subcommand::Cardinals => cardinals::run(wallet), } } } diff --git a/src/subcommand/wallet/cardinals.rs b/src/subcommand/wallet/cardinals.rs index 0a13006ad7..626cd67e3a 100644 --- a/src/subcommand/wallet/cardinals.rs +++ b/src/subcommand/wallet/cardinals.rs @@ -6,15 +6,11 @@ pub struct CardinalUtxo { pub amount: u64, } -pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - - index.update()?; - +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let unspent_outputs = wallet.get_unspent_outputs()?; - let inscribed_utxos = index - .get_inscriptions(&unspent_outputs)? + let inscribed_utxos = wallet + .get_inscriptions()? .keys() .map(|satpoint| satpoint.outpoint) .collect::>(); diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs index d1d9a8d4c4..157b8e2e73 100644 --- a/src/subcommand/wallet/outputs.rs +++ b/src/subcommand/wallet/outputs.rs @@ -6,11 +6,7 @@ pub struct Output { pub amount: u64, } -pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { - let index = Index::open(&options)?; - - index.update()?; - +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let mut outputs = Vec::new(); for (output, amount) in wallet.get_unspent_outputs()? { outputs.push(Output { From 38571af9fbc51fa5df81284b61117e27d818eb20 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 5 Jan 2024 20:28:38 +0100 Subject: [PATCH 16/74] fix merge --- src/subcommand/server.rs | 14 +++++++++++--- tests/json_api.rs | 2 +- tests/lib.rs | 10 ++++++++-- tests/wallet/send.rs | 2 +- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index c029eb2fa7..c27e51863a 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2542,9 +2542,17 @@ mod tests { transaction: txid.to_string(), sat_ranges: None, inscriptions: Vec::new(), - runes: vec![(Rune(RUNE), 340282366920938463463374607431768211455)] - .into_iter() - .collect(), + runes: vec![( + SpacedRune { + rune: Rune(RUNE), + spacers: 0 + }, + Pile { + amount: 340282366920938463463374607431768211455, + divisibility: 1, + symbol: None, + } + )], } ); } diff --git a/tests/json_api.rs b/tests/json_api.rs index 1a86fd1b47..0b46b92552 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -323,7 +323,7 @@ fn get_output() { InscriptionId { txid, index: 1 }, InscriptionId { txid, index: 2 }, ], - runes: BTreeMap::new(), + runes: Vec::new(), } ); } diff --git a/tests/lib.rs b/tests/lib.rs index f1d3ad25a4..7943344e40 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -14,8 +14,14 @@ use { rarity::Rarity, subcommand::runes::RuneInfo, templates::{ - block::BlockJson, inscription::InscriptionJson, inscriptions::InscriptionsJson, - output::OutputJson, rune::RuneJson, runes::RunesJson, sat::SatJson, status::StatusHtml, + block::BlockJson, + inscription::InscriptionJson, + inscriptions::InscriptionsJson, + output::OutputJson, + rune::RuneJson, + runes::RunesJson, + sat::SatJson, + status::StatusJson, }, Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone, SatPoint, }, diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 61c7f8d3fc..e82272b923 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -260,7 +260,7 @@ fn splitting_merged_inscriptions_is_possible() { index: 2 }, ], - runes: BTreeMap::new(), + runes: Vec::new(), } ); From c1d36051a17330b7c27fc0465f465b906c1a248b Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 5 Jan 2024 20:42:32 +0100 Subject: [PATCH 17/74] Etch no index --- src/lib.rs | 2 +- src/subcommand/wallet.rs | 18 ++++++++++++++++++ src/subcommand/wallet/etch.rs | 5 +---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 83a6030180..e15dfca50c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ use { }, sysinfo::System, tempfile::TempDir, - templates::{InscriptionJson, OutputJson, StatusJson}, + templates::{InscriptionJson, OutputJson, StatusJson, RuneJson}, tokio::{runtime::Runtime, task}, }; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 5c8840bddd..e5eb44ecaa 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -275,6 +275,24 @@ impl Wallet { Ok(self.get_inscription(inscription_id)?.satpoint) } + pub(crate) fn get_rune_info(&self, rune: Rune) -> Result> { + let response = self + .ord_http_client + .get( + self + .ord_api_url + .join(&format!("/rune/{}", SpacedRune { rune, spacers: 0 })) + .unwrap(), + ) + .send()?; + + if response.status().is_client_error() { + return Ok(None); + } + + Ok(serde_json::from_str(&response.text()?)?) + } + pub(crate) fn get_runic_outputs(&self) -> Result> { let mut runic_outputs = BTreeSet::new(); for output in self.get_unspent_outputs()?.keys() { diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 8d29474a06..ee1e9786cd 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -27,15 +27,12 @@ impl Etch { "`ord wallet etch` requires index created with `--index-runes` flag", ); - let index = Index::open(&options)?; - index.update()?; - let SpacedRune { rune, spacers } = self.rune; let count = wallet.bitcoin_rpc_client.get_block_count()?; ensure!( - index.rune(rune)?.is_none(), + wallet.get_rune_info(rune)?.is_none(), "rune `{}` has already been etched", rune, ); From 080998f6214e6d18a7b93d6c4cae59c71e70c94b Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 5 Jan 2024 20:47:07 +0100 Subject: [PATCH 18/74] Send no index --- src/subcommand/wallet.rs | 9 +++++++-- src/subcommand/wallet/send.rs | 10 ++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index e5eb44ecaa..e5306d955d 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -275,7 +275,10 @@ impl Wallet { Ok(self.get_inscription(inscription_id)?.satpoint) } - pub(crate) fn get_rune_info(&self, rune: Rune) -> Result> { + pub(crate) fn get_rune_info( + &self, + rune: Rune, + ) -> Result)>> { let response = self .ord_http_client .get( @@ -290,7 +293,9 @@ impl Wallet { return Ok(None); } - Ok(serde_json::from_str(&response.text()?)?) + let rune_json: RuneJson = serde_json::from_str(&response.text()?)?; + + Ok(Some((rune_json.id, rune_json.entry, rune_json.parent))) } pub(crate) fn get_runic_outputs(&self) -> Result> { diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index c69f656178..4b152823a3 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -25,10 +25,6 @@ impl Send { .clone() .require_network(options.chain().network())?; - let index = Index::open(&options)?; - - index.update()?; - let unspent_outputs = wallet.get_unspent_outputs()?; let locked_outputs = wallet.get_locked_outputs()?; @@ -49,7 +45,6 @@ impl Send { address, decimal, self.fee_rate, - &index, inscriptions, rune, runic_outputs, @@ -157,7 +152,6 @@ impl Send { address: Address, decimal: Decimal, fee_rate: FeeRate, - index: &Index, inscriptions: BTreeMap, spaced_rune: SpacedRune, runic_outputs: BTreeSet, @@ -171,8 +165,8 @@ impl Send { Self::lock_non_cardinal_outputs(wallet, &inscriptions, &runic_outputs, unspent_outputs)?; - let (id, entry, _parent) = index - .rune(spaced_rune.rune)? + let (id, entry, _parent) = wallet + .get_rune_info(spaced_rune.rune)? .with_context(|| format!("rune `{}` has not been etched", spaced_rune.rune))?; let amount = decimal.to_amount(entry.divisibility)?; From b2742857f0bdd83f93fbfcc51722bd34e51d4532 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 6 Jan 2024 02:19:59 +0100 Subject: [PATCH 19/74] Stuck --- src/index.rs | 66 +++++++++++++++++++------ src/subcommand/preview.rs | 3 ++ src/subcommand/server.rs | 2 +- src/subcommand/wallet.rs | 59 ++++++++++++++++++---- src/subcommand/wallet/etch.rs | 10 ++-- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/inscribe/batch.rs | 12 ++--- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 14 +++--- src/subcommand/wallet/transactions.rs | 2 +- 10 files changed, 126 insertions(+), 46 deletions(-) diff --git a/src/index.rs b/src/index.rs index 70fc97d298..41fc5f7382 100644 --- a/src/index.rs +++ b/src/index.rs @@ -206,6 +206,7 @@ pub struct Index { impl Index { pub fn open(options: &Options) -> Result { + println!("Opening................................."); let client = options.bitcoin_rpc_client(None)?; let path = options @@ -3355,13 +3356,29 @@ 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 tempdir = TempDir::new().unwrap(); + let cookie_file = tempdir.path().join("cookie"); + + fs::write(&cookie_file, "username:password").unwrap(); + + let command: Vec = vec![ + "ord".into(), + "--data-dir".into(), + tempdir.path().into(), + "--cookie-file".into(), + cookie_file.into(), + "--chain=regtest".into(), + "--rpc-url".into(), + context.options.rpc_url.clone().unwrap().into(), + ]; + + let options = Options::try_parse_from(command.into_iter()).unwrap(); Arguments { - options: context.options.clone(), + options: options.clone(), subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { name: "ord".into(), + no_sync: false, subcommand: crate::subcommand::wallet::Subcommand::Create( crate::subcommand::wallet::create::Create { passphrase: "".into(), @@ -3372,22 +3389,41 @@ mod tests { .run() .unwrap(); + let tempdir = TempDir::new().unwrap(); + let cookie_file = tempdir.path().join("cookie"); + + fs::write(&cookie_file, "username:password").unwrap(); + + let command: Vec = vec![ + "ord".into(), + "--data-dir".into(), + tempdir.path().into(), + "--cookie-file".into(), + cookie_file.into(), + "--chain=regtest".into(), + "--rpc-url".into(), + context.options.rpc_url.unwrap().into(), + ]; + + let options = Options::try_parse_from(command.into_iter()).unwrap(); + context.rpc_server.mine_blocks(1); - let wallet = crate::subcommand::wallet::Wallet { - bitcoin_rpc_client: crate::subcommand::wallet::bitcoin_rpc_client_for_wallet( - "ord".into(), - &context.options, - ) - .unwrap(), - chain: context.options.chain(), - ord_api_url: "127.0.0.1:8080".parse().unwrap(), - ord_http_client: reqwest::blocking::Client::new(), - wallet_name: "ord".into(), - }; + let output = Arguments { + options: options.clone(), + subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + name: "ord".into(), + no_sync: true, + subcommand: crate::subcommand::wallet::Subcommand::Balance, + }), + } + .run() + .err() + .unwrap() + .to_string(); assert_regex_match!( - wallet.get_unspent_outputs().unwrap_err().to_string(), + format!("{output}"), r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" ); } diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 77e9f96861..83ca99a418 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -87,6 +87,7 @@ impl Preview { options: options.clone(), subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { name: "ord".into(), + no_sync: false, subcommand: crate::subcommand::wallet::Subcommand::Create( crate::subcommand::wallet::create::Create { passphrase: "".into(), @@ -113,6 +114,7 @@ impl Preview { options: options.clone(), subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), + no_sync: false, subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: None, cbor_metadata: None, @@ -146,6 +148,7 @@ impl Preview { options: options.clone(), subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), + no_sync: false, subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: Some(batch), cbor_metadata: None, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index f50c6b1317..fa9b114836 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -171,7 +171,7 @@ pub(crate) struct Server { )] pub(crate) decompress: bool, #[arg(long, alias = "nosync", help = "Do not update the index.")] - no_sync: bool, + pub(crate) no_sync: bool, } impl Server { diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index e5306d955d..47bba8c92a 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -33,6 +33,8 @@ pub mod transactions; pub(crate) struct WalletCommand { #[arg(long, default_value = "ord", help = "Use wallet named .")] pub(crate) name: String, + #[arg(long, alias = "nosync", help = "Do not update index.")] + pub(crate) no_sync: bool, #[command(subcommand)] pub(crate) subcommand: Subcommand, } @@ -90,6 +92,7 @@ impl WalletCommand { redirect_http_to_https: false, enable_json_api: true, decompress: false, + no_sync: self.no_sync, } .run(options, index, handle) .unwrap() @@ -97,8 +100,6 @@ impl WalletCommand { } let wallet = Wallet { - // TODO: this won't work for create and restore - bitcoin_rpc_client: bitcoin_rpc_client_for_wallet(self.name.clone(), &options)?, chain: options.chain(), ord_api_url, ord_http_client: { @@ -112,9 +113,10 @@ impl WalletCommand { builder.build()? }, wallet_name: self.name.clone(), + options: options.clone(), }; - match self.subcommand { + let result = match self.subcommand { Subcommand::Balance => balance::run(wallet), Subcommand::Create(create) => create.run(wallet, options), Subcommand::Etch(etch) => etch.run(wallet, options), @@ -127,24 +129,62 @@ impl WalletCommand { Subcommand::Transactions(transactions) => transactions.run(wallet), Subcommand::Outputs => outputs::run(wallet), Subcommand::Cardinals => cardinals::run(wallet), - } + }; + + LISTENERS + .lock() + .unwrap() + .iter() + .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); + + result } } pub(crate) struct Wallet { - pub(crate) bitcoin_rpc_client: Client, // TODO: make this a function instead pub(crate) chain: Chain, pub(crate) ord_api_url: Url, pub(crate) ord_http_client: reqwest::blocking::Client, // TODO: make async instead of blocking pub(crate) wallet_name: String, + pub(crate) options: Options, } impl Wallet { + pub(crate) fn bitcoin_client(&self, _create: bool) -> Result { + let client = check_version( + self + .options + .bitcoin_rpc_client(Some(self.wallet_name.clone()))?, + )?; + + if !client.list_wallets()?.contains(&self.wallet_name) { + client.load_wallet(&self.wallet_name)?; + } + + 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!("wallet \"{}\" contains unexpected output descriptors, and does not appear to be an `ord` wallet, create a new wallet with `ord wallet create`", self.wallet_name); + } + + Ok(client) + } + pub(crate) fn get_unspent_outputs(&self) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( self - .bitcoin_rpc_client + .bitcoin_client(false)? .list_unspent(None, None, None, None, None)? .into_iter() .map(|utxo| { @@ -162,7 +202,7 @@ impl Wallet { outpoint, Amount::from_sat( self - .bitcoin_rpc_client + .bitcoin_client(false)? .get_raw_transaction(&outpoint.txid, None)? .output[TryInto::::try_into(outpoint.vout).unwrap()] .value, @@ -171,6 +211,7 @@ impl Wallet { } for output in utxos.keys() { + println!("{output}"); self.get_output(output)?; } @@ -365,7 +406,7 @@ impl Wallet { Ok( self - .bitcoin_rpc_client + .bitcoin_client(false)? .call::>("listlockunspent", &[])? .into_iter() .map(|outpoint| OutPoint::new(outpoint.txid, outpoint.vout)) @@ -376,7 +417,7 @@ impl Wallet { pub(crate) fn get_change_address(&self) -> Result
{ Ok( self - .bitcoin_rpc_client + .bitcoin_client(false)? .call::>("getrawchangeaddress", &["bech32m".into()]) .context("could not get change addresses from wallet")? .require_network(self.chain.network())?, diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index ee1e9786cd..6151047a1c 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -29,7 +29,7 @@ impl Etch { let SpacedRune { rune, spacers } = self.rune; - let count = wallet.bitcoin_rpc_client.get_block_count()?; + let count = wallet.bitcoin_client(false)?.get_block_count()?; ensure!( wallet.get_rune_info(rune)?.is_none(), @@ -104,23 +104,23 @@ impl Etch { .map(|satpoint| satpoint.outpoint) .collect::>(); - if !wallet.bitcoin_rpc_client.lock_unspent(&inscriptions)? { + if !wallet.bitcoin_client(false)?.lock_unspent(&inscriptions)? { bail!("failed to lock UTXOs"); } let unsigned_transaction = fund_raw_transaction( - &wallet.bitcoin_rpc_client, + &wallet.bitcoin_client(false)?, self.fee_rate, &unfunded_transaction, )?; let signed_transaction = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; let transaction = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .send_raw_transaction(&signed_transaction)?; Ok(Box::new(Output { diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index bc544781db..33d91f5bc4 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -242,7 +242,7 @@ impl Inscribe { id: parent_id, location: satpoint, tx_out: wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .get_raw_transaction(&satpoint.outpoint.txid, None) .expect("parent transaction not found in index") .output diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index 7a70262f5e..a732e56adc 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -66,13 +66,13 @@ impl Batch { } let signed_commit_tx = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .sign_raw_transaction_with_wallet(&commit_tx, None, None)? .hex; let signed_reveal_tx = if self.parent_info.is_some() { wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .sign_raw_transaction_with_wallet( &reveal_tx, Some( @@ -101,11 +101,11 @@ impl Batch { } let commit = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .send_raw_transaction(&signed_commit_tx)?; let reveal = match wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .send_raw_transaction(&signed_reveal_tx) { Ok(txid) => txid, @@ -454,11 +454,11 @@ impl Batch { let recovery_private_key = PrivateKey::new(recovery_key_pair.to_inner().secret_key(), network); let info = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; let response = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .import_descriptors(ImportDescriptors { descriptor: format!("rawtr({})#{}", recovery_private_key.to_wif(), info.checksum), timestamp: Timestamp::Now, diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 31d12b19fa..652fcbda4e 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -7,7 +7,7 @@ pub struct Output { pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let address = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; Ok(Box::new(Output { address })) diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 4b152823a3..e95a4ed578 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -91,11 +91,11 @@ impl Send { .build_transaction()?; let signed_tx = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let txid = wallet.bitcoin_rpc_client.send_raw_transaction(&signed_tx)?; + let txid = wallet.bitcoin_client(false)?.send_raw_transaction(&signed_tx)?; Ok(Box::new(Output { transaction: txid })) } @@ -118,7 +118,7 @@ impl Send { .cloned() .collect::>(); - if !wallet.bitcoin_rpc_client.lock_unspent(&locked_outputs)? { + if !wallet.bitcoin_client(false)?.lock_unspent(&locked_outputs)? { bail!("failed to lock UTXOs"); } @@ -131,7 +131,7 @@ impl Send { address: Address, fee_rate: FeeRate, ) -> Result { - Ok(wallet.bitcoin_rpc_client.call( + Ok(wallet.bitcoin_client(false)?.call( "sendtoaddress", &[ address.to_string().into(), // 1. address @@ -245,16 +245,16 @@ impl Send { }; let unsigned_transaction = - fund_raw_transaction(&wallet.bitcoin_rpc_client, fee_rate, &unfunded_transaction)?; + fund_raw_transaction(&wallet.bitcoin_client(false)?, fee_rate, &unfunded_transaction)?; let signed_transaction = wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; Ok( wallet - .bitcoin_rpc_client + .bitcoin_client(false)? .send_raw_transaction(&signed_transaction)?, ) } diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index 041c76d754..b60a7f1eb4 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -14,7 +14,7 @@ pub struct Output { impl Transactions { pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - let client = wallet.bitcoin_rpc_client; + let client = wallet.bitcoin_client(false)?; let mut output = Vec::new(); for tx in client.list_transactions( From c6c1abe5b188d745ab1cec0cf1494b896d07d93a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 6 Jan 2024 16:51:06 +0100 Subject: [PATCH 20/74] Add in index for output JSON --- src/index.rs | 9 +++++++++ src/subcommand/server.rs | 8 ++++++++ src/templates/output.rs | 3 +++ tests/json_api.rs | 1 + tests/wallet/send.rs | 1 + 5 files changed, 22 insertions(+) diff --git a/src/index.rs b/src/index.rs index 91fb5e6cb8..05eb464e1a 100644 --- a/src/index.rs +++ b/src/index.rs @@ -395,6 +395,15 @@ impl Index { Ok(true) } + pub(crate) fn contains(&self, output: &OutPoint) -> Result { + let rtx = self.database.begin_read()?; + let outpoint_to_value = rtx.open_table(OUTPOINT_TO_VALUE)?; + + let contains = outpoint_to_value.get(&output.store())?.is_some(); + + Ok(contains) + } + pub(crate) fn has_rune_index(&self) -> bool { self.index_runes } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index d1e60c57e5..8994ec0b7a 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -543,6 +543,8 @@ impl Server { ) -> ServerResult { let list = index.list(outpoint)?; + let mut in_index = false; + let output = if outpoint == OutPoint::null() || outpoint == unbound_outpoint() { let mut value = 0; @@ -552,11 +554,15 @@ impl Server { } } + in_index = true; + TxOut { value, script_pubkey: ScriptBuf::new(), } } else { + in_index = index.contains(&outpoint)?; + index .get_transaction(outpoint.txid)? .ok_or_not_found(|| format!("output {outpoint}"))? @@ -577,6 +583,7 @@ impl Server { server_config.chain, output, inscriptions, + in_index, runes .into_iter() .map(|(spaced_rune, pile)| (spaced_rune.rune, pile.amount)) @@ -2548,6 +2555,7 @@ mod tests { address: None, transaction: txid.to_string(), sat_ranges: None, + in_index: true, inscriptions: Vec::new(), runes: vec![(Rune(RUNE), 340282366920938463463374607431768211455)] .into_iter() diff --git a/src/templates/output.rs b/src/templates/output.rs index 08064e073a..39fea2b446 100644 --- a/src/templates/output.rs +++ b/src/templates/output.rs @@ -17,6 +17,7 @@ pub struct OutputJson { pub address: Option, pub transaction: String, pub sat_ranges: Option>, + pub in_index: bool, pub inscriptions: Vec, pub runes: BTreeMap, } @@ -28,6 +29,7 @@ impl OutputJson { chain: Chain, output: TxOut, inscriptions: Vec, + in_index: bool, runes: BTreeMap, ) -> Self { Self { @@ -43,6 +45,7 @@ impl OutputJson { Some(List::Unspent(ranges)) => Some(ranges), _ => None, }, + in_index, inscriptions, } } diff --git a/tests/json_api.rs b/tests/json_api.rs index fc0d471a19..b8b95622d1 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -323,6 +323,7 @@ fn get_output() { InscriptionId { txid, index: 1 }, InscriptionId { txid, index: 2 }, ], + in_index: true, runes: BTreeMap::new(), } ); diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 61c7f8d3fc..638bd9e819 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -260,6 +260,7 @@ fn splitting_merged_inscriptions_is_possible() { index: 2 }, ], + in_index: true, runes: BTreeMap::new(), } ); From 6a159304922f52244825d382af629e3a369965d1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 6 Jan 2024 16:59:58 +0100 Subject: [PATCH 21/74] Add check sync --- src/subcommand/wallet.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 47bba8c92a..80c0870139 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -276,11 +276,15 @@ impl Wallet { .get(self.ord_api_url.join(&format!("/output/{output}")).unwrap()) .send()?; - if response.status().is_client_error() { + let not_found = response.status().is_client_error(); + + let output_json: OutputJson = serde_json::from_str(&response.text()?)?; + + if not_found || !output_json.in_index { bail!("output in Bitcoin Core wallet but not in ord index: {output}"); } - Ok(serde_json::from_str(&response.text()?)?) + Ok(output_json) } fn get_inscription(&self, inscription_id: InscriptionId) -> Result { From fbb2e99533efa7d2319a3cdd3dc7061e1a7b33bb Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 6 Jan 2024 17:11:56 +0100 Subject: [PATCH 22/74] Fix unsynced index test --- src/index.rs | 32 +++++++++++++++----------------- src/lib.rs | 2 +- src/subcommand/server.rs | 2 +- src/subcommand/wallet.rs | 9 ++++----- src/subcommand/wallet/send.rs | 16 ++++++++++++---- tests/lib.rs | 10 ++-------- 6 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/index.rs b/src/index.rs index dcfb3b885b..6299bedb13 100644 --- a/src/index.rs +++ b/src/index.rs @@ -206,7 +206,6 @@ pub struct Index { impl Index { pub fn open(options: &Options) -> Result { - println!("Opening................................."); let client = options.bitcoin_rpc_client(None)?; let path = options @@ -2083,7 +2082,6 @@ mod tests { use { super::*, crate::index::testing::Context, - bitcoin::secp256k1::rand::{self, RngCore}, }; #[test] @@ -3383,6 +3381,8 @@ mod tests { let options = Options::try_parse_from(command.into_iter()).unwrap(); + context.rpc_server.mine_blocks(2); + Arguments { options: options.clone(), subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { @@ -3416,23 +3416,21 @@ mod tests { let options = Options::try_parse_from(command.into_iter()).unwrap(); - context.rpc_server.mine_blocks(1); - - let output = Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { - name: "ord".into(), - no_sync: true, - subcommand: crate::subcommand::wallet::Subcommand::Balance, - }), - } - .run() - .err() - .unwrap() - .to_string(); + context.rpc_server.mine_blocks(2); assert_regex_match!( - format!("{output}"), + Arguments { + options: options.clone(), + subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + name: "ord".into(), + no_sync: true, + subcommand: crate::subcommand::wallet::Subcommand::Balance, + }), + } + .run() + .err() + .unwrap() + .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 e15dfca50c..9ebc498ab7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ use { }, sysinfo::System, tempfile::TempDir, - templates::{InscriptionJson, OutputJson, StatusJson, RuneJson}, + templates::{InscriptionJson, OutputJson, RuneJson, StatusJson}, tokio::{runtime::Runtime, task}, }; diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 9e09b21fc7..bbc7bbfc47 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -543,7 +543,7 @@ impl Server { ) -> ServerResult { let list = index.list(outpoint)?; - let mut in_index = false; + let in_index: bool; let output = if outpoint == OutPoint::null() || outpoint == unbound_outpoint() { let mut value = 0; diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 80c0870139..3631572a7c 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -211,7 +211,6 @@ impl Wallet { } for output in utxos.keys() { - println!("{output}"); self.get_output(output)?; } @@ -249,7 +248,7 @@ impl Wallet { } for output in utxos.keys() { - if let Some(sat_ranges) = self.get_output(&output)?.sat_ranges { + if let Some(sat_ranges) = self.get_output(output)?.sat_ranges { let mut offset = 0; for (start, end) in sat_ranges { if start <= sat.n() && sat.n() < end { @@ -280,7 +279,7 @@ impl Wallet { let output_json: OutputJson = serde_json::from_str(&response.text()?)?; - if not_found || !output_json.in_index { + if not_found || !output_json.in_index { bail!("output in Bitcoin Core wallet but not in ord index: {output}"); } @@ -308,7 +307,7 @@ impl Wallet { pub(crate) fn get_inscriptions(&self) -> Result> { let mut inscriptions = BTreeMap::new(); for output in self.get_unspent_outputs()?.keys() { - for inscription in self.get_output(&output)?.inscriptions { + for inscription in self.get_output(output)?.inscriptions { inscriptions.insert(self.get_inscription_satpoint(inscription)?, inscription); } } @@ -393,7 +392,7 @@ impl Wallet { let status: StatusJson = serde_json::from_str( &self .ord_http_client - .get(self.ord_api_url.join(&format!("/status")).unwrap()) + .get(self.ord_api_url.join("/status").unwrap()) .send()? .text()?, )?; diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index e95a4ed578..d0675c4f0c 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -95,7 +95,9 @@ impl Send { .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let txid = wallet.bitcoin_client(false)?.send_raw_transaction(&signed_tx)?; + let txid = wallet + .bitcoin_client(false)? + .send_raw_transaction(&signed_tx)?; Ok(Box::new(Output { transaction: txid })) } @@ -118,7 +120,10 @@ impl Send { .cloned() .collect::>(); - if !wallet.bitcoin_client(false)?.lock_unspent(&locked_outputs)? { + if !wallet + .bitcoin_client(false)? + .lock_unspent(&locked_outputs)? + { bail!("failed to lock UTXOs"); } @@ -244,8 +249,11 @@ impl Send { ], }; - let unsigned_transaction = - fund_raw_transaction(&wallet.bitcoin_client(false)?, fee_rate, &unfunded_transaction)?; + let unsigned_transaction = fund_raw_transaction( + &wallet.bitcoin_client(false)?, + fee_rate, + &unfunded_transaction, + )?; let signed_transaction = wallet .bitcoin_client(false)? diff --git a/tests/lib.rs b/tests/lib.rs index 9e0bce37bf..50f835f7f0 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -14,14 +14,8 @@ use { rarity::Rarity, subcommand::runes::RuneInfo, templates::{ - block::BlockJson, - inscription::InscriptionJson, - inscriptions::InscriptionsJson, - output::OutputJson, - rune::RuneJson, - runes::RunesJson, - sat::SatJson, - status::StatusJson, + block::BlockJson, inscription::InscriptionJson, inscriptions::InscriptionsJson, + output::OutputJson, rune::RuneJson, runes::RunesJson, sat::SatJson, status::StatusJson, }, Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone, SatPoint, }, From 296bd3834fd1a9d5e73e6f4b58ea87da1704d8c2 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 6 Jan 2024 17:32:16 +0100 Subject: [PATCH 23/74] Remove unused functions and only pass in wallet --- src/subcommand/wallet.rs | 90 +++++++------------------ src/subcommand/wallet/create.rs | 4 +- src/subcommand/wallet/etch.rs | 4 +- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/inscribe/batch.rs | 4 +- src/subcommand/wallet/inscriptions.rs | 4 +- src/subcommand/wallet/restore.rs | 4 +- src/subcommand/wallet/send.rs | 4 +- 8 files changed, 38 insertions(+), 78 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 3631572a7c..1e26e7f5af 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -100,7 +100,7 @@ impl WalletCommand { } let wallet = Wallet { - chain: options.chain(), + options: options.clone(), ord_api_url, ord_http_client: { let mut headers = header::HeaderMap::new(); @@ -112,20 +112,19 @@ impl WalletCommand { builder.build()? }, - wallet_name: self.name.clone(), - options: options.clone(), + name: self.name.clone(), }; let result = match self.subcommand { Subcommand::Balance => balance::run(wallet), - Subcommand::Create(create) => create.run(wallet, options), - Subcommand::Etch(etch) => etch.run(wallet, options), + Subcommand::Create(create) => create.run(wallet), + Subcommand::Etch(etch) => etch.run(wallet), Subcommand::Inscribe(inscribe) => inscribe.run(wallet), - Subcommand::Inscriptions => inscriptions::run(wallet, options), + Subcommand::Inscriptions => inscriptions::run(wallet), Subcommand::Receive => receive::run(wallet), - Subcommand::Restore(restore) => restore.run(wallet, options), + Subcommand::Restore(restore) => restore.run(wallet), Subcommand::Sats(sats) => sats.run(wallet), - Subcommand::Send(send) => send.run(wallet, options), + Subcommand::Send(send) => send.run(wallet), Subcommand::Transactions(transactions) => transactions.run(wallet), Subcommand::Outputs => outputs::run(wallet), Subcommand::Cardinals => cardinals::run(wallet), @@ -142,11 +141,10 @@ impl WalletCommand { } pub(crate) struct Wallet { - pub(crate) chain: Chain, + pub(crate) name: String, + pub(crate) options: Options, pub(crate) ord_api_url: Url, pub(crate) ord_http_client: reqwest::blocking::Client, // TODO: make async instead of blocking - pub(crate) wallet_name: String, - pub(crate) options: Options, } impl Wallet { @@ -154,11 +152,11 @@ impl Wallet { let client = check_version( self .options - .bitcoin_rpc_client(Some(self.wallet_name.clone()))?, + .bitcoin_rpc_client(Some(self.name.clone()))?, )?; - if !client.list_wallets()?.contains(&self.wallet_name) { - client.load_wallet(&self.wallet_name)?; + if !client.list_wallets()?.contains(&self.name) { + client.load_wallet(&self.name)?; } let descriptors = client.list_descriptors(None)?.descriptors; @@ -174,12 +172,16 @@ impl Wallet { .count(); if tr != 2 || descriptors.len() != 2 + rawtr { - bail!("wallet \"{}\" contains unexpected output descriptors, and does not appear to be an `ord` wallet, create a new wallet with `ord wallet create`", self.wallet_name); + bail!("wallet \"{}\" contains unexpected output descriptors, and does not appear to be an `ord` wallet, create a new wallet with `ord wallet create`", self.name); } Ok(client) } + pub(crate) fn chain(&self) -> Chain { + self.options.chain() + } + pub(crate) fn get_unspent_outputs(&self) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( @@ -376,18 +378,6 @@ impl Wallet { ) } - pub(crate) fn get_rune_balances( - &self, - utxos: &BTreeMap, - ) -> Result> { - let mut rune_balances = Vec::new(); - for output in utxos.keys() { - rune_balances.append(&mut self.get_runes_balances_for_output(output)?); - } - - Ok(rune_balances) - } - pub(crate) fn get_server_status(&self) -> Result { let status: StatusJson = serde_json::from_str( &self @@ -423,20 +413,20 @@ impl Wallet { .bitcoin_client(false)? .call::>("getrawchangeaddress", &["bech32m".into()]) .context("could not get change addresses from wallet")? - .require_network(self.chain.network())?, + .require_network(self.chain().network())?, ) } - pub(crate) fn initialize(&self, options: &Options, seed: [u8; 64]) -> Result { - check_version(options.bitcoin_rpc_client(None)?)?.create_wallet( - &self.wallet_name, + pub(crate) fn initialize(&self, seed: [u8; 64]) -> Result { + check_version(self.options.bitcoin_rpc_client(None)?)?.create_wallet( + &self.name, None, Some(true), None, None, )?; - let network = self.chain.network(); + let network = self.chain().network(); let secp = Secp256k1::new(); @@ -455,7 +445,6 @@ impl Wallet { for change in [false, true] { self.derive_and_import_descriptor( - options, &secp, (fingerprint, derivation_path.clone()), derived_private_key, @@ -468,7 +457,6 @@ impl Wallet { fn derive_and_import_descriptor( &self, - options: &Options, secp: &Secp256k1, origin: (Fingerprint, DerivationPath), derived_private_key: ExtendedPrivKey, @@ -490,8 +478,9 @@ impl Wallet { let desc = Descriptor::new_tr(public_key, None)?; - options - .bitcoin_rpc_client(Some(self.wallet_name.clone()))? + self + .options + .bitcoin_rpc_client(Some(self.name.clone()))? .import_descriptors(ImportDescriptors { descriptor: desc.to_string_with_secret(&key_map), timestamp: Timestamp::Now, @@ -506,35 +495,6 @@ impl Wallet { } } -pub(crate) fn bitcoin_rpc_client_for_wallet( - wallet_name: String, - options: &Options, -) -> Result { - let client = check_version(options.bitcoin_rpc_client(Some(wallet_name.clone()))?)?; - - if !client.list_wallets()?.contains(&wallet_name) { - client.load_wallet(&wallet_name)?; - } - - 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!("wallet \"{}\" contains unexpected output descriptors, and does not appear to be an `ord` wallet, create a new wallet with `ord wallet create`", wallet_name); - } - - Ok(client) -} - pub(crate) fn check_version(client: Client) -> Result { const MIN_VERSION: usize = 240000; diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 2cedcdbd8f..de6de14c67 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -17,13 +17,13 @@ pub(crate) struct Create { } impl Create { - pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { let mut entropy = [0; 16]; rand::thread_rng().fill_bytes(&mut entropy); let mnemonic = Mnemonic::from_entropy(&entropy)?; - wallet.initialize(&options, mnemonic.to_seed(self.passphrase.clone()))?; + wallet.initialize(mnemonic.to_seed(self.passphrase.clone()))?; Ok(Box::new(Output { mnemonic, diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 6151047a1c..ada676eac4 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -21,7 +21,7 @@ pub struct Output { } impl Etch { - pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { ensure!( wallet.get_server_status()?.rune_index, "`ord wallet etch` requires index created with `--index-runes` flag", @@ -38,7 +38,7 @@ impl Etch { ); let minimum_at_height = - Rune::minimum_at_height(options.chain(), Height(u32::try_from(count).unwrap() + 1)); + Rune::minimum_at_height(wallet.chain(), Height(u32::try_from(count).unwrap() + 1)); ensure!( rune >= minimum_at_height, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 33d91f5bc4..f9e69aab41 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -117,7 +117,7 @@ impl Inscribe { let runic_utxos = wallet.get_runic_outputs()?; - let chain = wallet.chain; + let chain = wallet.chain(); let postage; let destinations; diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index a732e56adc..3109204be5 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -49,7 +49,7 @@ impl Batch { let (commit_tx, reveal_tx, recovery_key_pair, total_fees) = self .create_batch_inscription_transactions( wallet_inscriptions, - wallet.chain, + wallet.chain(), locked_utxos.clone(), runic_utxos, utxos.clone(), @@ -97,7 +97,7 @@ impl Batch { }; if !self.no_backup { - Self::backup_recovery_key(wallet, recovery_key_pair, wallet.chain.network())?; + Self::backup_recovery_key(wallet, recovery_key_pair, wallet.chain().network())?; } let commit = wallet diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs index 36cd5c8871..720bae0c37 100644 --- a/src/subcommand/wallet/inscriptions.rs +++ b/src/subcommand/wallet/inscriptions.rs @@ -8,12 +8,12 @@ pub struct Output { pub postage: u64, } -pub(crate) fn run(wallet: Wallet, options: Options) -> SubcommandResult { +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let unspent_outputs = wallet.get_unspent_outputs()?; let inscriptions = wallet.get_inscriptions()?; - let explorer = match options.chain() { + let explorer = match wallet.chain() { Chain::Mainnet => "https://ordinals.com/inscription/", Chain::Regtest => "http://localhost/inscription/", Chain::Signet => "https://signet.ordinals.com/inscription/", diff --git a/src/subcommand/wallet/restore.rs b/src/subcommand/wallet/restore.rs index 6b838d246a..114283dd5b 100644 --- a/src/subcommand/wallet/restore.rs +++ b/src/subcommand/wallet/restore.rs @@ -13,8 +13,8 @@ pub(crate) struct Restore { } impl Restore { - pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { - wallet.initialize(&options, self.mnemonic.to_seed(self.passphrase))?; + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + wallet.initialize(self.mnemonic.to_seed(self.passphrase))?; Ok(Box::new(Empty {})) } diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index d0675c4f0c..24759b93de 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -19,11 +19,11 @@ pub struct Output { } impl Send { - pub(crate) fn run(self, wallet: Wallet, options: Options) -> SubcommandResult { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { let address = self .address .clone() - .require_network(options.chain().network())?; + .require_network(wallet.chain().network())?; let unspent_outputs = wallet.get_unspent_outputs()?; From 8ecb107988f6c9d815487ce03b67a931fcaeafab Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 7 Jan 2024 23:16:42 +0100 Subject: [PATCH 24/74] stashing --- src/subcommand/wallet.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 1e26e7f5af..c2fb047539 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -134,7 +134,7 @@ impl WalletCommand { .lock() .unwrap() .iter() - .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); + .for_each(|handle| handle.shutdown()); result } @@ -149,11 +149,7 @@ pub(crate) struct Wallet { impl Wallet { pub(crate) fn bitcoin_client(&self, _create: bool) -> Result { - let client = check_version( - self - .options - .bitcoin_rpc_client(Some(self.name.clone()))?, - )?; + let client = check_version(self.options.bitcoin_rpc_client(Some(self.name.clone()))?)?; if !client.list_wallets()?.contains(&self.name) { client.load_wallet(&self.name)?; @@ -282,7 +278,7 @@ impl Wallet { let output_json: OutputJson = serde_json::from_str(&response.text()?)?; if not_found || !output_json.in_index { - bail!("output in Bitcoin Core wallet but not in ord index: {output}"); + // bail!("output in Bitcoin Core wallet but not in ord index: {output}"); } Ok(output_json) From 5d08319aea6b2f26784cbabd97105f818d406f0e Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 8 Jan 2024 17:32:19 +0100 Subject: [PATCH 25/74] Remove bool --- src/subcommand/wallet.rs | 116 ++++++++++++++---------- src/subcommand/wallet/etch.rs | 10 +- src/subcommand/wallet/inscribe.rs | 2 +- src/subcommand/wallet/inscribe/batch.rs | 12 +-- src/subcommand/wallet/receive.rs | 2 +- src/subcommand/wallet/send.rs | 24 ++--- src/subcommand/wallet/transactions.rs | 2 +- tests/etch.rs | 2 +- tests/wallet/inscribe.rs | 2 +- 9 files changed, 96 insertions(+), 76 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index b765361bc6..acb7369b8d 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -109,16 +109,6 @@ impl WalletCommand { let wallet = Wallet { options, ord_url, - ord_http_client: { - let mut headers = header::HeaderMap::new(); - headers.insert( - header::ACCEPT, - header::HeaderValue::from_static("application/json"), - ); - let builder = reqwest::blocking::ClientBuilder::new().default_headers(headers); - - builder.build()? - }, name: self.name.clone(), }; @@ -151,11 +141,10 @@ pub(crate) struct Wallet { pub(crate) name: String, pub(crate) options: Options, pub(crate) ord_url: Url, - pub(crate) ord_http_client: reqwest::blocking::Client, // TODO: make async instead of blocking } impl Wallet { - pub(crate) fn bitcoin_client(&self, _create: bool) -> Result { + pub(crate) fn bitcoin_client(&self) -> Result { let client = check_version(self.options.bitcoin_rpc_client(Some(self.name.clone()))?)?; if !client.list_wallets()?.contains(&self.name) { @@ -181,15 +170,69 @@ impl Wallet { Ok(client) } - pub(crate) fn chain(&self) -> Chain { - self.options.chain() + pub(crate) fn ord_client(&self) -> Result { + let mut headers = header::HeaderMap::new(); + headers.insert( + header::ACCEPT, + header::HeaderValue::from_static("application/json"), + ); + let builder = reqwest::blocking::ClientBuilder::new().default_headers(headers); + + let client = builder.build().map_err(|err| anyhow!(err))?; + + let status: StatusJson = serde_json::from_str( + &client + .get(self.ord_url.join("/status").unwrap()) + .send()? + .text()?, + )?; + + if let Some(ord_height) = status.height { + let bitcoin_height = self.bitcoin_client()?.get_blockchain_info()?.blocks; + if (ord_height as u64) < bitcoin_height { + return Err(anyhow!( + "unsynced: ord is at {ord_height} and bitcoind is at {bitcoin_height}" + )); + } + } + + Ok(client) + } + + pub(crate) fn get_server_status(&self) -> Result { + let status: StatusJson = serde_json::from_str( + &self + .ord_client()? + .get(self.ord_url.join("/status").unwrap()) + .send()? + .text()?, + )?; + + Ok(status) + } + + fn get_output(&self, output: &OutPoint) -> Result { + let response = self + .ord_client()? + .get(self.ord_url.join(&format!("/output/{output}")).unwrap()) + .send()?; + + let output_json: OutputJson = serde_json::from_str(&response.text()?)?; + + // dbg!(&output_json); + + if !output_json.in_index { + // bail!("output in Bitcoin Core wallet but not in ord index: {output}"); + } + + Ok(output_json) } pub(crate) fn get_unspent_outputs(&self) -> Result> { let mut utxos = BTreeMap::new(); utxos.extend( self - .bitcoin_client(false)? + .bitcoin_client()? .list_unspent(None, None, None, None, None)? .into_iter() .map(|utxo| { @@ -207,7 +250,7 @@ impl Wallet { outpoint, Amount::from_sat( self - .bitcoin_client(false)? + .bitcoin_client()? .get_raw_transaction(&outpoint.txid, None)? .output[TryInto::::try_into(outpoint.vout).unwrap()] .value, @@ -274,26 +317,9 @@ impl Wallet { ))) } - fn get_output(&self, output: &OutPoint) -> Result { - let response = self - .ord_http_client - .get(self.ord_url.join(&format!("/output/{output}")).unwrap()) - .send()?; - - let not_found = response.status().is_client_error(); - - let output_json: OutputJson = serde_json::from_str(&response.text()?)?; - - if not_found || !output_json.in_index { - // bail!("output in Bitcoin Core wallet but not in ord index: {output}"); - } - - Ok(output_json) - } - fn get_inscription(&self, inscription_id: InscriptionId) -> Result { let response = self - .ord_http_client + .ord_client()? .get( self .ord_url @@ -329,7 +355,7 @@ impl Wallet { rune: Rune, ) -> Result)>> { let response = self - .ord_http_client + .ord_client()? .get( self .ord_url @@ -381,18 +407,6 @@ impl Wallet { ) } - pub(crate) fn get_server_status(&self) -> Result { - let status: StatusJson = serde_json::from_str( - &self - .ord_http_client - .get(self.ord_url.join("/status").unwrap()) - .send()? - .text()?, - )?; - - Ok(status) - } - pub(crate) fn get_locked_outputs(&self) -> Result> { #[derive(Deserialize)] pub(crate) struct JsonOutPoint { @@ -402,7 +416,7 @@ impl Wallet { Ok( self - .bitcoin_client(false)? + .bitcoin_client()? .call::>("listlockunspent", &[])? .into_iter() .map(|outpoint| OutPoint::new(outpoint.txid, outpoint.vout)) @@ -413,7 +427,7 @@ impl Wallet { pub(crate) fn get_change_address(&self) -> Result
{ Ok( self - .bitcoin_client(false)? + .bitcoin_client()? .call::>("getrawchangeaddress", &["bech32m".into()]) .context("could not get change addresses from wallet")? .require_network(self.chain().network())?, @@ -496,6 +510,10 @@ impl Wallet { Ok(()) } + + pub(crate) fn chain(&self) -> Chain { + self.options.chain() + } } pub(crate) fn check_version(client: Client) -> Result { diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index ada676eac4..9f10139557 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -29,7 +29,7 @@ impl Etch { let SpacedRune { rune, spacers } = self.rune; - let count = wallet.bitcoin_client(false)?.get_block_count()?; + let count = wallet.bitcoin_client()?.get_block_count()?; ensure!( wallet.get_rune_info(rune)?.is_none(), @@ -104,23 +104,23 @@ impl Etch { .map(|satpoint| satpoint.outpoint) .collect::>(); - if !wallet.bitcoin_client(false)?.lock_unspent(&inscriptions)? { + if !wallet.bitcoin_client()?.lock_unspent(&inscriptions)? { bail!("failed to lock UTXOs"); } let unsigned_transaction = fund_raw_transaction( - &wallet.bitcoin_client(false)?, + &wallet.bitcoin_client()?, self.fee_rate, &unfunded_transaction, )?; let signed_transaction = wallet - .bitcoin_client(false)? + .bitcoin_client()? .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; let transaction = wallet - .bitcoin_client(false)? + .bitcoin_client()? .send_raw_transaction(&signed_transaction)?; Ok(Box::new(Output { diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index f9e69aab41..7aa7fcf77d 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -242,7 +242,7 @@ impl Inscribe { id: parent_id, location: satpoint, tx_out: wallet - .bitcoin_client(false)? + .bitcoin_client()? .get_raw_transaction(&satpoint.outpoint.txid, None) .expect("parent transaction not found in index") .output diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index 3109204be5..ac0f3bc46d 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -66,13 +66,13 @@ impl Batch { } let signed_commit_tx = wallet - .bitcoin_client(false)? + .bitcoin_client()? .sign_raw_transaction_with_wallet(&commit_tx, None, None)? .hex; let signed_reveal_tx = if self.parent_info.is_some() { wallet - .bitcoin_client(false)? + .bitcoin_client()? .sign_raw_transaction_with_wallet( &reveal_tx, Some( @@ -101,11 +101,11 @@ impl Batch { } let commit = wallet - .bitcoin_client(false)? + .bitcoin_client()? .send_raw_transaction(&signed_commit_tx)?; let reveal = match wallet - .bitcoin_client(false)? + .bitcoin_client()? .send_raw_transaction(&signed_reveal_tx) { Ok(txid) => txid, @@ -454,11 +454,11 @@ impl Batch { let recovery_private_key = PrivateKey::new(recovery_key_pair.to_inner().secret_key(), network); let info = wallet - .bitcoin_client(false)? + .bitcoin_client()? .get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; let response = wallet - .bitcoin_client(false)? + .bitcoin_client()? .import_descriptors(ImportDescriptors { descriptor: format!("rawtr({})#{}", recovery_private_key.to_wif(), info.checksum), timestamp: Timestamp::Now, diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs index 652fcbda4e..8cd8c3dc59 100644 --- a/src/subcommand/wallet/receive.rs +++ b/src/subcommand/wallet/receive.rs @@ -7,7 +7,7 @@ pub struct Output { pub(crate) fn run(wallet: Wallet) -> SubcommandResult { let address = wallet - .bitcoin_client(false)? + .bitcoin_client()? .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; Ok(Box::new(Output { address })) diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 24759b93de..c82162c914 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -33,6 +33,11 @@ impl Send { let runic_outputs = wallet.get_runic_outputs()?; + // dbg!(&unspent_outputs); + // dbg!(&locked_outputs); + // dbg!(&inscriptions); + // dbg!(&runic_outputs); + let satpoint = match self.outgoing { Outgoing::Amount(amount) => { Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; @@ -91,12 +96,12 @@ impl Send { .build_transaction()?; let signed_tx = wallet - .bitcoin_client(false)? + .bitcoin_client()? .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; let txid = wallet - .bitcoin_client(false)? + .bitcoin_client()? .send_raw_transaction(&signed_tx)?; Ok(Box::new(Output { transaction: txid })) @@ -121,7 +126,7 @@ impl Send { .collect::>(); if !wallet - .bitcoin_client(false)? + .bitcoin_client()? .lock_unspent(&locked_outputs)? { bail!("failed to lock UTXOs"); @@ -136,7 +141,7 @@ impl Send { address: Address, fee_rate: FeeRate, ) -> Result { - Ok(wallet.bitcoin_client(false)?.call( + Ok(wallet.bitcoin_client()?.call( "sendtoaddress", &[ address.to_string().into(), // 1. address @@ -249,20 +254,17 @@ impl Send { ], }; - let unsigned_transaction = fund_raw_transaction( - &wallet.bitcoin_client(false)?, - fee_rate, - &unfunded_transaction, - )?; + let unsigned_transaction = + fund_raw_transaction(&wallet.bitcoin_client()?, fee_rate, &unfunded_transaction)?; let signed_transaction = wallet - .bitcoin_client(false)? + .bitcoin_client()? .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; Ok( wallet - .bitcoin_client(false)? + .bitcoin_client()? .send_raw_transaction(&signed_transaction)?, ) } diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs index b60a7f1eb4..7977bd89ad 100644 --- a/src/subcommand/wallet/transactions.rs +++ b/src/subcommand/wallet/transactions.rs @@ -14,7 +14,7 @@ pub struct Output { impl Transactions { pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - let client = wallet.bitcoin_client(false)?; + let client = wallet.bitcoin_client()?; let mut output = Vec::new(); for tx in client.list_transactions( diff --git a/tests/etch.rs b/tests/etch.rs index a434dd8f23..ad7fb5e4ea 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -401,7 +401,7 @@ fn send_inscription_does_not_select_runic_utxos() { .rpc_server(&rpc_server) .run_and_deserialize_output::(); - assert_eq!(output.cardinal, 0); + // assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 10000); assert_eq!(output.runic, Some(10000)); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 91a9b6a537..2b525f3ed8 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1620,7 +1620,7 @@ fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { .write("foo.txt", "FOO") .rpc_server(&rpc_server) .expected_exit_code(1) - .expected_stderr("error: could not find sat `5000000000`\n") + .expected_stderr("error: could not find sat `5000000000` in wallet outputs\n") .run_and_extract_stdout(); } From 32e191bd7393d74a757b3017dc2c887f6b03cfea Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 8 Jan 2024 17:32:40 +0100 Subject: [PATCH 26/74] fmt --- src/index.rs | 35 ++++++++++++++++------------------- src/subcommand/wallet/send.rs | 9 ++------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/index.rs b/src/index.rs index 7de1b7b98b..3f1f14882a 100644 --- a/src/index.rs +++ b/src/index.rs @@ -2079,10 +2079,7 @@ impl Index { #[cfg(test)] mod tests { - use { - super::*, - crate::index::testing::Context, - }; + use {super::*, crate::index::testing::Context}; #[test] fn height_limit() { @@ -3418,21 +3415,21 @@ mod tests { context.rpc_server.mine_blocks(2); -// assert_regex_match!( -// Arguments { -// options: options.clone(), -// subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { -// name: "ord".into(), -// no_sync: true, -// subcommand: crate::subcommand::wallet::Subcommand::Balance, -// }), -// } -// .run() -// .err() -// .unwrap() -// .to_string(), -// r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" -// ); + // assert_regex_match!( + // Arguments { + // options: options.clone(), + // subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + // name: "ord".into(), + // no_sync: true, + // subcommand: crate::subcommand::wallet::Subcommand::Balance, + // }), + // } + // .run() + // .err() + // .unwrap() + // .to_string(), + // r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" + // ); } } diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index c82162c914..f72cd78bb6 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -100,9 +100,7 @@ impl Send { .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let txid = wallet - .bitcoin_client()? - .send_raw_transaction(&signed_tx)?; + let txid = wallet.bitcoin_client()?.send_raw_transaction(&signed_tx)?; Ok(Box::new(Output { transaction: txid })) } @@ -125,10 +123,7 @@ impl Send { .cloned() .collect::>(); - if !wallet - .bitcoin_client()? - .lock_unspent(&locked_outputs)? - { + if !wallet.bitcoin_client()?.lock_unspent(&locked_outputs)? { bail!("failed to lock UTXOs"); } From cbeb7fb58952a630d7387f390211d17bd312d453 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 8 Jan 2024 17:33:06 +0100 Subject: [PATCH 27/74] quick fix --- tests/etch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/etch.rs b/tests/etch.rs index ad7fb5e4ea..a434dd8f23 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -401,7 +401,7 @@ fn send_inscription_does_not_select_runic_utxos() { .rpc_server(&rpc_server) .run_and_deserialize_output::(); - // assert_eq!(output.cardinal, 0); + assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 10000); assert_eq!(output.runic, Some(10000)); From d40d14cc0ec75b027b5d6ced18facbc15154ba0d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 10 Jan 2024 16:08:06 +0100 Subject: [PATCH 28/74] It works, just have to fix one test --- src/index.rs | 98 ++++++----------------------------- src/subcommand/wallet.rs | 37 +++++++------ src/subcommand/wallet/send.rs | 5 -- tests/etch.rs | 2 + 4 files changed, 39 insertions(+), 103 deletions(-) diff --git a/src/index.rs b/src/index.rs index 3f1f14882a..502fb69a4e 100644 --- a/src/index.rs +++ b/src/index.rs @@ -24,7 +24,7 @@ use { StorageError, Table, TableDefinition, TableHandle, WriteTransaction, }, std::{ - collections::{BTreeSet, HashMap}, + collections::HashMap, io::{BufWriter, Write}, sync::{Mutex, Once}, }, @@ -902,32 +902,6 @@ impl Index { Ok(entries) } - pub(crate) fn get_rune_balance(&self, outpoint: OutPoint, id: RuneId) -> Result { - let rtx = self.database.begin_read()?; - - let outpoint_to_balances = rtx.open_table(OUTPOINT_TO_RUNE_BALANCES)?; - - let Some(balances) = outpoint_to_balances.get(&outpoint.store())? else { - return Ok(0); - }; - - let balances_buffer = balances.value(); - - let mut i = 0; - while i < balances_buffer.len() { - let (balance_id, length) = runes::varint::decode(&balances_buffer[i..]); - i += length; - let (amount, length) = runes::varint::decode(&balances_buffer[i..]); - i += length; - - if RuneId::try_from(balance_id).unwrap() == id { - return Ok(amount); - } - } - - Ok(0) - } - pub(crate) fn get_rune_balances_for_outpoint( &self, outpoint: OutPoint, @@ -969,22 +943,6 @@ impl Index { Ok(balances) } - pub(crate) fn get_runic_outputs(&self, outpoints: &[OutPoint]) -> Result> { - let rtx = self.database.begin_read()?; - - let outpoint_to_balances = rtx.open_table(OUTPOINT_TO_RUNE_BALANCES)?; - - let mut runic = BTreeSet::new(); - - for outpoint in outpoints { - if outpoint_to_balances.get(&outpoint.store())?.is_some() { - runic.insert(*outpoint); - } - } - - Ok(runic) - } - pub(crate) fn get_rune_balance_map(&self) -> Result>> { let outpoint_balances = self.get_rune_balances()?; @@ -1627,28 +1585,6 @@ impl Index { )) } - pub(crate) fn get_inscriptions( - &self, - utxos: &BTreeMap, - ) -> Result> { - let rtx = self.database.begin_read()?; - - let mut result = BTreeMap::new(); - - let satpoint_to_sequence_number = rtx.open_multimap_table(SATPOINT_TO_SEQUENCE_NUMBER)?; - let sequence_number_to_inscription_entry = - rtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ENTRY)?; - for utxo in utxos.keys() { - result.extend(Self::inscriptions_on_output( - &satpoint_to_sequence_number, - &sequence_number_to_inscription_entry, - *utxo, - )?); - } - - Ok(result) - } - pub(crate) fn get_inscriptions_paginated( &self, page_size: usize, @@ -3357,7 +3293,7 @@ mod tests { } } - #[test] + // #[test] fn unsynced_index_fails() { for context in Context::configurations() { let tempdir = TempDir::new().unwrap(); @@ -3415,21 +3351,21 @@ mod tests { context.rpc_server.mine_blocks(2); - // assert_regex_match!( - // Arguments { - // options: options.clone(), - // subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { - // name: "ord".into(), - // no_sync: true, - // subcommand: crate::subcommand::wallet::Subcommand::Balance, - // }), - // } - // .run() - // .err() - // .unwrap() - // .to_string(), - // r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" - // ); + assert_regex_match!( + Arguments { + options: options.clone(), + subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + name: "ord".into(), + no_sync: true, + subcommand: crate::subcommand::wallet::Subcommand::Balance, + }), + } + .run() + .err() + .unwrap() + .to_string(), + r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" + ); } } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index acb7369b8d..5aa63429c0 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -10,6 +10,7 @@ use { }, bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, fee_rate::FeeRate, + http::StatusCode, miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, reqwest::{header, Url}, transaction_builder::TransactionBuilder, @@ -176,24 +177,28 @@ impl Wallet { header::ACCEPT, header::HeaderValue::from_static("application/json"), ); - let builder = reqwest::blocking::ClientBuilder::new().default_headers(headers); - let client = builder.build().map_err(|err| anyhow!(err))?; + let client = reqwest::blocking::ClientBuilder::new() + .default_headers(headers) + .build() + .map_err(|err| anyhow!(err))?; - let status: StatusJson = serde_json::from_str( - &client - .get(self.ord_url.join("/status").unwrap()) - .send()? - .text()?, - )?; + let chain_block_count = self.bitcoin_client()?.get_block_count().unwrap() + 1; - if let Some(ord_height) = status.height { - let bitcoin_height = self.bitcoin_client()?.get_blockchain_info()?.blocks; - if (ord_height as u64) < bitcoin_height { - return Err(anyhow!( - "unsynced: ord is at {ord_height} and bitcoind is at {bitcoin_height}" - )); + for i in 0.. { + let response = client + .get(self.ord_url.join("/blockcount").unwrap()) + .send()?; + + assert_eq!(response.status(), StatusCode::OK); + + if response.text()?.parse::().unwrap() >= chain_block_count { + break; + } else if i == 20 { + panic!("wallet failed to synchronize index"); } + + thread::sleep(Duration::from_millis(25)); } Ok(client) @@ -219,10 +224,8 @@ impl Wallet { let output_json: OutputJson = serde_json::from_str(&response.text()?)?; - // dbg!(&output_json); - if !output_json.in_index { - // bail!("output in Bitcoin Core wallet but not in ord index: {output}"); + bail!("output in Bitcoin Core wallet but not in ord index: {output}"); } Ok(output_json) diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index f72cd78bb6..59ae5237a3 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -33,11 +33,6 @@ impl Send { let runic_outputs = wallet.get_runic_outputs()?; - // dbg!(&unspent_outputs); - // dbg!(&locked_outputs); - // dbg!(&inscriptions); - // dbg!(&runic_outputs); - let satpoint = match self.outgoing { Outgoing::Amount(amount) => { Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; diff --git a/tests/etch.rs b/tests/etch.rs index a434dd8f23..982aadcdac 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -9,6 +9,8 @@ fn flag_is_required() { .network(Network::Regtest) .build(); + create_wallet(&rpc_server); + CommandBuilder::new(format!( "--regtest wallet etch --rune {} --divisibility 39 --fee-rate 1 --supply 1000 --symbol ยข", Rune(RUNE), From f8480ba53e43379d82d5e4666bb7cfb1e14d9ce4 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 10 Jan 2024 16:19:41 +0100 Subject: [PATCH 29/74] quick fix --- src/index.rs | 72 ++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/index.rs b/src/index.rs index f062c7db10..21131f1fe1 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3329,7 +3329,7 @@ mod tests { } } - // #[test] + #[test] fn unsynced_index_fails() { for context in Context::configurations() { let tempdir = TempDir::new().unwrap(); @@ -3367,41 +3367,41 @@ mod tests { .run() .unwrap(); - let tempdir = TempDir::new().unwrap(); - let cookie_file = tempdir.path().join("cookie"); - - fs::write(&cookie_file, "username:password").unwrap(); - - let command: Vec = vec![ - "ord".into(), - "--data-dir".into(), - tempdir.path().into(), - "--cookie-file".into(), - cookie_file.into(), - "--chain=regtest".into(), - "--rpc-url".into(), - context.options.rpc_url.unwrap().into(), - ]; - - let options = Options::try_parse_from(command.into_iter()).unwrap(); - - context.rpc_server.mine_blocks(2); - - assert_regex_match!( - Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { - name: "ord".into(), - no_sync: true, - subcommand: crate::subcommand::wallet::Subcommand::Balance, - }), - } - .run() - .err() - .unwrap() - .to_string(), - r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" - ); + // let tempdir = TempDir::new().unwrap(); + // let cookie_file = tempdir.path().join("cookie"); + + // fs::write(&cookie_file, "username:password").unwrap(); + + // let command: Vec = vec![ + // "ord".into(), + // "--data-dir".into(), + // tempdir.path().into(), + // "--cookie-file".into(), + // cookie_file.into(), + // "--chain=regtest".into(), + // "--rpc-url".into(), + // context.options.rpc_url.unwrap().into(), + // ]; + + // let options = Options::try_parse_from(command.into_iter()).unwrap(); + + // context.rpc_server.mine_blocks(2); + + // assert_regex_match!( + // Arguments { + // options: options.clone(), + // subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + // name: "ord".into(), + // no_sync: true, + // subcommand: crate::subcommand::wallet::Subcommand::Balance, + // }), + // } + // .run() + // .err() + // .unwrap() + // .to_string(), + // r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" + // ); } } From e6de26cb578376cce2ad2f345b86ccb3b8a9850a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 10 Jan 2024 16:59:52 +0100 Subject: [PATCH 30/74] Fix preview test --- src/subcommand/preview.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index cd7c41c77e..16d0326e3a 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -64,7 +64,7 @@ impl Preview { let options = Options { chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(bitcoin_data_dir), + bitcoin_data_dir: Some(bitcoin_data_dir.clone()), data_dir: tmpdir.path().into(), rpc_url: Some(format!("127.0.0.1:{rpc_port}")), index_sats: true, @@ -110,6 +110,17 @@ impl Preview { if let Some(files) = self.files { for file in files { + let tmpdir = TempDir::new()?; + + let options = Options { + chain_argument: Chain::Regtest, + bitcoin_data_dir: Some(bitcoin_data_dir.clone()), + data_dir: tmpdir.path().into(), + rpc_url: Some(format!("127.0.0.1:{rpc_port}")), + index_sats: true, + ..Options::default() + }; + Arguments { options: options.clone(), subcommand: Subcommand::Wallet(super::wallet::WalletCommand { @@ -144,6 +155,16 @@ impl Preview { if let Some(batches) = self.batches { for batch in batches { + let tmpdir = TempDir::new()?; + + let options = Options { + chain_argument: Chain::Regtest, + bitcoin_data_dir: Some(bitcoin_data_dir.clone()), + data_dir: tmpdir.path().into(), + rpc_url: Some(format!("127.0.0.1:{rpc_port}")), + index_sats: true, + ..Options::default() + }; Arguments { options: options.clone(), subcommand: Subcommand::Wallet(super::wallet::WalletCommand { @@ -176,6 +197,17 @@ impl Preview { } } + let tmpdir = TempDir::new()?; + + let options = Options { + chain_argument: Chain::Regtest, + bitcoin_data_dir: Some(bitcoin_data_dir), + data_dir: tmpdir.path().into(), + rpc_url: Some(format!("127.0.0.1:{rpc_port}")), + index_sats: true, + ..Options::default() + }; + if let Some(blocktime) = self.blocktime { eprintln!( "Mining blocks every {}...", From c297ab4076f4e79a595b607acdaccaaa2fda057f Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 10 Jan 2024 17:43:06 +0100 Subject: [PATCH 31/74] It works but tests are slow now --- src/index.rs | 90 +++++++++++++++++++--------------------- src/subcommand/wallet.rs | 26 +++++++----- 2 files changed, 57 insertions(+), 59 deletions(-) diff --git a/src/index.rs b/src/index.rs index 21131f1fe1..b28c87b315 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3337,23 +3337,20 @@ mod tests { fs::write(&cookie_file, "username:password").unwrap(); - let command: Vec = vec![ - "ord".into(), - "--data-dir".into(), - tempdir.path().into(), - "--cookie-file".into(), - cookie_file.into(), - "--chain=regtest".into(), - "--rpc-url".into(), - context.options.rpc_url.clone().unwrap().into(), - ]; - - let options = Options::try_parse_from(command.into_iter()).unwrap(); + let options = Options { + chain_argument: Chain::Regtest, + bitcoin_data_dir: Some(tempdir.path().into()), + data_dir: tempdir.path().into(), + rpc_url: context.options.rpc_url.clone(), + index_sats: true, + cookie_file: Some(cookie_file.clone()), + ..Options::default() + }; context.rpc_server.mine_blocks(2); Arguments { - options: options.clone(), + options, subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { name: "ord".into(), no_sync: false, @@ -3367,41 +3364,38 @@ mod tests { .run() .unwrap(); - // let tempdir = TempDir::new().unwrap(); - // let cookie_file = tempdir.path().join("cookie"); - - // fs::write(&cookie_file, "username:password").unwrap(); - - // let command: Vec = vec![ - // "ord".into(), - // "--data-dir".into(), - // tempdir.path().into(), - // "--cookie-file".into(), - // cookie_file.into(), - // "--chain=regtest".into(), - // "--rpc-url".into(), - // context.options.rpc_url.unwrap().into(), - // ]; - - // let options = Options::try_parse_from(command.into_iter()).unwrap(); - - // context.rpc_server.mine_blocks(2); - - // assert_regex_match!( - // Arguments { - // options: options.clone(), - // subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { - // name: "ord".into(), - // no_sync: true, - // subcommand: crate::subcommand::wallet::Subcommand::Balance, - // }), - // } - // .run() - // .err() - // .unwrap() - // .to_string(), - // r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" - // ); + let tempdir = TempDir::new().unwrap(); + let cookie_file = tempdir.path().join("cookie"); + + fs::write(&cookie_file, "username:password").unwrap(); + + let options = Options { + chain_argument: Chain::Regtest, + bitcoin_data_dir: Some(tempdir.path().into()), + data_dir: tempdir.path().into(), + rpc_url: context.options.rpc_url.clone(), + index_sats: true, + cookie_file: Some(cookie_file.clone()), + ..Options::default() + }; + + context.rpc_server.mine_blocks(2); + + assert_regex_match!( + Arguments { + options, + subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + name: "ord".into(), + no_sync: true, + subcommand: crate::subcommand::wallet::Subcommand::Balance, + }), + } + .run() + .err() + .unwrap() + .to_string(), + r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" + ); } } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 5aa63429c0..58949a3e50 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -108,6 +108,7 @@ impl WalletCommand { } let wallet = Wallet { + no_sync: self.no_sync, options, ord_url, name: self.name.clone(), @@ -140,6 +141,7 @@ impl WalletCommand { pub(crate) struct Wallet { pub(crate) name: String, + pub(crate) no_sync: bool, pub(crate) options: Options, pub(crate) ord_url: Url, } @@ -185,20 +187,22 @@ impl Wallet { let chain_block_count = self.bitcoin_client()?.get_block_count().unwrap() + 1; - for i in 0.. { - let response = client - .get(self.ord_url.join("/blockcount").unwrap()) - .send()?; + if !self.no_sync { + for i in 0.. { + let response = client + .get(self.ord_url.join("/blockcount").unwrap()) + .send()?; - assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.status(), StatusCode::OK); - if response.text()?.parse::().unwrap() >= chain_block_count { - break; - } else if i == 20 { - panic!("wallet failed to synchronize index"); - } + if response.text()?.parse::().unwrap() >= chain_block_count { + break; + } else if i == 20 { + panic!("wallet failed to synchronize to index"); + } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(25)); + } } Ok(client) From e3f8114c8b232977708d39de6f6c7a04896c5b02 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 10 Jan 2024 18:21:46 +0100 Subject: [PATCH 32/74] Some cleanup --- src/subcommand/wallet.rs | 64 +++++++++++++++++--------------- src/subcommand/wallet/balance.rs | 4 +- src/subcommand/wallet/etch.rs | 4 +- src/subcommand/wallet/sats.rs | 7 ++-- src/subcommand/wallet/send.rs | 4 +- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 58949a3e50..3363f45b49 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -142,7 +142,7 @@ impl WalletCommand { pub(crate) struct Wallet { pub(crate) name: String, pub(crate) no_sync: bool, - pub(crate) options: Options, + pub(crate) options: Options, // Only need for bitcoin_rpc_client() and chain() pub(crate) ord_url: Url, } @@ -208,18 +208,6 @@ impl Wallet { Ok(client) } - pub(crate) fn get_server_status(&self) -> Result { - let status: StatusJson = serde_json::from_str( - &self - .ord_client()? - .get(self.ord_url.join("/status").unwrap()) - .send()? - .text()?, - )?; - - Ok(status) - } - fn get_output(&self, output: &OutPoint) -> Result { let response = self .ord_client()? @@ -266,18 +254,17 @@ impl Wallet { } for output in utxos.keys() { - self.get_output(output)?; + self.get_output(output)?; //check that wallet outputs in ord index } Ok(utxos) } pub(crate) fn get_output_sat_ranges(&self) -> Result)>> { - if !self.get_server_status()?.sat_index { - return Err(anyhow!( - "index must be built with `--index-sats` to use `--sat`" - )); - } + ensure!( + self.check_sat_index()?, + "index must be built with `--index-sats` to use `--sat`" + ); let mut output_sat_ranges = Vec::new(); for output in self.get_unspent_outputs()?.keys() { @@ -296,11 +283,10 @@ impl Wallet { sat: Sat, utxos: &BTreeMap, ) -> Result { - if !self.get_server_status()?.sat_index { - return Err(anyhow!( - "index must be built with `--index-sats` to use `--sat`" - )); - } + ensure!( + self.check_sat_index()?, + "index must be built with `--index-sats` to use `--sat`" + ); for output in utxos.keys() { if let Some(sat_ranges) = self.get_output(output)?.sat_ranges { @@ -357,7 +343,7 @@ impl Wallet { Ok(self.get_inscription(inscription_id)?.satpoint) } - pub(crate) fn get_rune_info( + pub(crate) fn get_rune( &self, rune: Rune, ) -> Result)>> { @@ -441,6 +427,30 @@ impl Wallet { ) } + pub(crate) fn get_server_status(&self) -> Result { + let status: StatusJson = serde_json::from_str( + &self + .ord_client()? + .get(self.ord_url.join("/status").unwrap()) + .send()? + .text()?, + )?; + + Ok(status) + } + + pub(crate) fn check_rune_index(&self) -> Result { + Ok(self.get_server_status()?.rune_index) + } + + pub(crate) fn check_sat_index(&self) -> Result { + Ok(self.get_server_status()?.sat_index) + } + + pub(crate) fn chain(&self) -> Chain { + self.options.chain() + } + pub(crate) fn initialize(&self, seed: [u8; 64]) -> Result { check_version(self.options.bitcoin_rpc_client(None)?)?.create_wallet( &self.name, @@ -517,10 +527,6 @@ impl Wallet { Ok(()) } - - pub(crate) fn chain(&self) -> Chain { - self.options.chain() - } } pub(crate) fn check_version(client: Client) -> Result { diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index d8c66d10ae..5d68d8a0b3 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -43,8 +43,8 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { Ok(Some(Box::new(Output { cardinal, ordinal, - runes: wallet.get_server_status()?.rune_index.then_some(runes), - runic: wallet.get_server_status()?.rune_index.then_some(runic), + runes: wallet.check_rune_index()?.then_some(runes), + runic: wallet.check_rune_index()?.then_some(runic), total: cardinal + ordinal + runic, }))) } diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index b77467640d..e290426ca6 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -23,7 +23,7 @@ pub struct Output { impl Etch { pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { ensure!( - wallet.get_server_status()?.rune_index, + wallet.check_rune_index()?, "`ord wallet etch` requires index created with `--index-runes` flag", ); @@ -32,7 +32,7 @@ impl Etch { let count = wallet.bitcoin_client()?.get_block_count()?; ensure!( - wallet.get_rune_info(rune)?.is_none(), + wallet.get_rune(rune)?.is_none(), "rune `{}` has already been etched", rune, ); diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index f8c536f80b..35d3ea5584 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -25,9 +25,10 @@ pub struct OutputRare { impl Sats { pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { - if !wallet.get_server_status()?.sat_index { - bail!("sats requires index created with `--index-sats` flag"); - } + ensure!( + wallet.check_sat_index()?, + "sats requires index created with `--index-sats` flag" + ); let utxos = wallet.get_output_sat_ranges()?; diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 5c1fdff645..305aadc59e 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -159,14 +159,14 @@ impl Send { wallet: &Wallet, ) -> Result { ensure!( - wallet.get_server_status()?.rune_index, + wallet.check_rune_index()?, "sending runes with `ord send` requires index created with `--index-runes` flag", ); Self::lock_non_cardinal_outputs(wallet, &inscriptions, &runic_outputs, unspent_outputs)?; let (id, entry, _parent) = wallet - .get_rune_info(spaced_rune.rune)? + .get_rune(spaced_rune.rune)? .with_context(|| format!("rune `{}` has not been etched", spaced_rune.rune))?; let amount = decimal.to_amount(entry.divisibility)?; From 5a80f9283515c55152d75751742ff401866f57a9 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 11 Jan 2024 22:15:23 +0100 Subject: [PATCH 33/74] quick fix --- tests/command_builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/command_builder.rs b/tests/command_builder.rs index 01fc621fe2..6f3bef18da 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -117,6 +117,8 @@ impl CommandBuilder { .to_str() .unwrap(), ]); + } else { + fs::write("~/.bitcoin/.cookie", "username:password").unwrap(); } command From 8585a546c451c2e3c4f34c68b220f35021f2a170 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 11 Jan 2024 22:39:50 +0100 Subject: [PATCH 34/74] quick fix --- tests/command_builder.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/command_builder.rs b/tests/command_builder.rs index 6f3bef18da..e917d6501f 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -118,7 +118,11 @@ impl CommandBuilder { .unwrap(), ]); } else { - fs::write("~/.bitcoin/.cookie", "username:password").unwrap(); + let bitcoin_dir = dirs::home_dir().unwrap().join(".bitcoin"); + if !bitcoin_dir.exists() { + fs::create_dir_all(bitcoin_dir.clone()).unwrap(); + fs::write(bitcoin_dir.join(".cookie"), "username:password").unwrap(); + } } command From df9b15e75b49184de420ef32b6fb465a33f97dde Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 11 Jan 2024 22:49:12 +0100 Subject: [PATCH 35/74] Place clippy --- src/index.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/index.rs b/src/index.rs index be12eddf3c..d463fa7c1d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -381,15 +381,6 @@ impl Index { self.durability = durability; } - pub(crate) fn contains(&self, output: &OutPoint) -> Result { - let rtx = self.database.begin_read()?; - let outpoint_to_value = rtx.open_table(OUTPOINT_TO_VALUE)?; - - let contains = outpoint_to_value.get(&output.store())?.is_some(); - - Ok(contains) - } - pub(crate) fn contains_output(&self, output: &OutPoint) -> Result { Ok( self From 39fe0d669bca0d9a2f48ec1061555ae004a2a1cb Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 11 Jan 2024 23:02:46 +0100 Subject: [PATCH 36/74] quick fix --- tests/command_builder.rs | 6 ------ tests/wallet/inscribe.rs | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/command_builder.rs b/tests/command_builder.rs index e917d6501f..01fc621fe2 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -117,12 +117,6 @@ impl CommandBuilder { .to_str() .unwrap(), ]); - } else { - let bitcoin_dir = dirs::home_dir().unwrap().join(".bitcoin"); - if !bitcoin_dir.exists() { - fs::create_dir_all(bitcoin_dir.clone()).unwrap(); - fs::write(bitcoin_dir.join(".cookie"), "username:password").unwrap(); - } } command diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 2b525f3ed8..0beb519640 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -740,11 +740,14 @@ fn cbor_metadata_appears_on_inscription_page() { #[test] fn error_message_when_parsing_json_metadata_is_reasonable() { + let rpc_server = test_bitcoincore_rpc::spawn(); + CommandBuilder::new( "wallet inscribe --fee-rate 1 --json-metadata metadata.json --file content.png", ) .write("content.png", [1; 520]) .write("metadata.json", "{") + .rpc_server(&rpc_server) .stderr_regex(".*failed to parse JSON metadata.*") .expected_exit_code(1) .run_and_extract_stdout(); @@ -752,11 +755,14 @@ fn error_message_when_parsing_json_metadata_is_reasonable() { #[test] fn error_message_when_parsing_cbor_metadata_is_reasonable() { + let rpc_server = test_bitcoincore_rpc::spawn(); + CommandBuilder::new( "wallet inscribe --fee-rate 1 --cbor-metadata metadata.cbor --file content.png", ) .write("content.png", [1; 520]) .write("metadata.cbor", [0x61]) + .rpc_server(&rpc_server) .stderr_regex(".*failed to parse CBOR metadata.*") .expected_exit_code(1) .run_and_extract_stdout(); From efeede57be03ee7b09cd62afdaad46389eec02e7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 11 Jan 2024 23:38:33 +0100 Subject: [PATCH 37/74] Clean up some bits here and there --- src/subcommand/wallet/inscribe.rs | 3 +-- src/subcommand/wallet/inscribe/batch.rs | 20 +++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 7aa7fcf77d..90675ba28d 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -162,12 +162,11 @@ impl Inscribe { .unwrap_or(TARGET_POSTAGE); (inscriptions, destinations) = batchfile.inscriptions( - chain, + &wallet, parent_info.as_ref().map(|info| info.tx_out.value), metadata, postage, self.compress, - &wallet, )?; mode = batchfile.mode; diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/subcommand/wallet/inscribe/batch.rs index bf8444c4c3..78b1607e46 100644 --- a/src/subcommand/wallet/inscribe/batch.rs +++ b/src/subcommand/wallet/inscribe/batch.rs @@ -97,7 +97,7 @@ impl Batch { }; if !self.no_backup { - Self::backup_recovery_key(wallet, recovery_key_pair, wallet.chain().network())?; + Self::backup_recovery_key(wallet, recovery_key_pair)?; } let commit = wallet @@ -446,12 +446,11 @@ impl Batch { Ok((unsigned_commit_tx, reveal_tx, recovery_key_pair, total_fees)) } - fn backup_recovery_key( - wallet: &Wallet, - recovery_key_pair: TweakedKeyPair, - network: Network, - ) -> Result { - let recovery_private_key = PrivateKey::new(recovery_key_pair.to_inner().secret_key(), network); + fn backup_recovery_key(wallet: &Wallet, recovery_key_pair: TweakedKeyPair) -> Result { + let recovery_private_key = PrivateKey::new( + recovery_key_pair.to_inner().secret_key(), + wallet.chain().network(), + ); let info = wallet .bitcoin_client()? @@ -591,12 +590,11 @@ impl Batchfile { pub(crate) fn inscriptions( &self, - chain: Chain, + wallet: &Wallet, parent_value: Option, metadata: Option>, postage: Amount, compress: bool, - wallet: &Wallet, ) -> Result<(Vec, Vec
)> { assert!(!self.inscriptions.is_empty()); @@ -623,7 +621,7 @@ impl Batchfile { let mut inscriptions = Vec::new(); for (i, entry) in self.inscriptions.iter().enumerate() { inscriptions.push(Inscription::from_file( - chain, + wallet.chain(), &entry.file, self.parent, if i == 0 { None } else { Some(pointer) }, @@ -649,7 +647,7 @@ impl Batchfile { |address| { address .clone() - .require_network(chain.network()) + .require_network(wallet.chain().network()) .map_err(|e| e.into()) }, ) From 66755df51314f0d23dc1be49f808a899f2bf0f8a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 13 Jan 2024 16:20:20 +0100 Subject: [PATCH 38/74] Move wallet into separate module --- src/index.rs | 14 +++--- src/lib.rs | 3 +- src/options.rs | 2 +- src/subcommand.rs | 1 - src/{subcommand => }/wallet.rs | 0 src/{subcommand => }/wallet/balance.rs | 0 src/{subcommand => }/wallet/cardinals.rs | 0 src/{subcommand => }/wallet/create.rs | 0 src/{subcommand => }/wallet/etch.rs | 0 src/{subcommand => }/wallet/inscribe.rs | 2 +- src/{subcommand => }/wallet/inscribe/batch.rs | 0 src/{subcommand => }/wallet/inscriptions.rs | 0 src/{subcommand => }/wallet/outputs.rs | 0 src/{subcommand => }/wallet/receive.rs | 0 src/{subcommand => }/wallet/restore.rs | 0 src/{subcommand => }/wallet/sats.rs | 0 src/{subcommand => }/wallet/send.rs | 2 +- .../wallet/transaction_builder.rs | 0 src/{subcommand => }/wallet/transactions.rs | 0 tests/etch.rs | 15 +++--- tests/lib.rs | 6 +-- tests/server.rs | 2 +- tests/wallet/balance.rs | 2 +- tests/wallet/cardinals.rs | 2 +- tests/wallet/create.rs | 2 +- tests/wallet/inscribe.rs | 12 +++-- tests/wallet/inscriptions.rs | 2 +- tests/wallet/outputs.rs | 2 +- tests/wallet/receive.rs | 4 +- tests/wallet/restore.rs | 2 +- tests/wallet/sats.rs | 2 +- tests/wallet/send.rs | 50 ++++++++++--------- tests/wallet/transactions.rs | 2 +- 33 files changed, 69 insertions(+), 60 deletions(-) rename src/{subcommand => }/wallet.rs (100%) rename src/{subcommand => }/wallet/balance.rs (100%) rename src/{subcommand => }/wallet/cardinals.rs (100%) rename src/{subcommand => }/wallet/create.rs (100%) rename src/{subcommand => }/wallet/etch.rs (100%) rename src/{subcommand => }/wallet/inscribe.rs (99%) rename src/{subcommand => }/wallet/inscribe/batch.rs (100%) rename src/{subcommand => }/wallet/inscriptions.rs (100%) rename src/{subcommand => }/wallet/outputs.rs (100%) rename src/{subcommand => }/wallet/receive.rs (100%) rename src/{subcommand => }/wallet/restore.rs (100%) rename src/{subcommand => }/wallet/sats.rs (100%) rename src/{subcommand => }/wallet/send.rs (98%) rename src/{subcommand => }/wallet/transaction_builder.rs (100%) rename src/{subcommand => }/wallet/transactions.rs (100%) diff --git a/src/index.rs b/src/index.rs index d463fa7c1d..5aaee1dc2b 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3353,14 +3353,12 @@ mod tests { Arguments { options, - subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + subcommand: Subcommand::Wallet(wallet::WalletCommand { name: "ord".into(), no_sync: false, - subcommand: crate::subcommand::wallet::Subcommand::Create( - crate::subcommand::wallet::create::Create { - passphrase: "".into(), - }, - ), + subcommand: wallet::Subcommand::Create(wallet::create::Create { + passphrase: "".into(), + }), }), } .run() @@ -3386,10 +3384,10 @@ mod tests { assert_regex_match!( Arguments { options, - subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { + subcommand: Subcommand::Wallet(wallet::WalletCommand { name: "ord".into(), no_sync: true, - subcommand: crate::subcommand::wallet::Subcommand::Balance, + subcommand: wallet::Subcommand::Balance, }), } .run() diff --git a/src/lib.rs b/src/lib.rs index 726c77d4b9..fffc5c3495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,7 @@ pub use self::{ runes::{Edict, Rune, RuneId, Runestone}, sat::Sat, sat_point::SatPoint, - subcommand::wallet::transaction_builder::{Target, TransactionBuilder}, + wallet::transaction_builder::{Target, TransactionBuilder}, }; #[cfg(test)] @@ -139,6 +139,7 @@ mod server_config; pub mod subcommand; mod tally; pub mod templates; +pub mod wallet; type Result = std::result::Result; diff --git a/src/options.rs b/src/options.rs index fc03adb9dd..884b6cee0d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -567,7 +567,7 @@ mod tests { ); } - fn parse_wallet_args(args: &str) -> (Options, subcommand::wallet::WalletCommand) { + fn parse_wallet_args(args: &str) -> (Options, wallet::WalletCommand) { match Arguments::try_parse_from(args.split_whitespace()) { Ok(arguments) => match arguments.subcommand { Subcommand::Wallet(wallet) => (arguments.options, wallet), diff --git a/src/subcommand.rs b/src/subcommand.rs index f9d965596d..72742ae3dd 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -14,7 +14,6 @@ pub mod subsidy; pub mod supply; pub mod teleburn; pub mod traits; -pub mod wallet; #[derive(Debug, Parser)] pub(crate) enum Subcommand { diff --git a/src/subcommand/wallet.rs b/src/wallet.rs similarity index 100% rename from src/subcommand/wallet.rs rename to src/wallet.rs diff --git a/src/subcommand/wallet/balance.rs b/src/wallet/balance.rs similarity index 100% rename from src/subcommand/wallet/balance.rs rename to src/wallet/balance.rs diff --git a/src/subcommand/wallet/cardinals.rs b/src/wallet/cardinals.rs similarity index 100% rename from src/subcommand/wallet/cardinals.rs rename to src/wallet/cardinals.rs diff --git a/src/subcommand/wallet/create.rs b/src/wallet/create.rs similarity index 100% rename from src/subcommand/wallet/create.rs rename to src/wallet/create.rs diff --git a/src/subcommand/wallet/etch.rs b/src/wallet/etch.rs similarity index 100% rename from src/subcommand/wallet/etch.rs rename to src/wallet/etch.rs diff --git a/src/subcommand/wallet/inscribe.rs b/src/wallet/inscribe.rs similarity index 99% rename from src/subcommand/wallet/inscribe.rs rename to src/wallet/inscribe.rs index 90675ba28d..a5a6ea4d58 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/wallet/inscribe.rs @@ -1,7 +1,7 @@ use { self::batch::{Batch, Batchfile, Mode}, super::*, - crate::subcommand::wallet::transaction_builder::Target, + wallet::transaction_builder::Target, bitcoin::{ blockdata::{opcodes, script}, key::PrivateKey, diff --git a/src/subcommand/wallet/inscribe/batch.rs b/src/wallet/inscribe/batch.rs similarity index 100% rename from src/subcommand/wallet/inscribe/batch.rs rename to src/wallet/inscribe/batch.rs diff --git a/src/subcommand/wallet/inscriptions.rs b/src/wallet/inscriptions.rs similarity index 100% rename from src/subcommand/wallet/inscriptions.rs rename to src/wallet/inscriptions.rs diff --git a/src/subcommand/wallet/outputs.rs b/src/wallet/outputs.rs similarity index 100% rename from src/subcommand/wallet/outputs.rs rename to src/wallet/outputs.rs diff --git a/src/subcommand/wallet/receive.rs b/src/wallet/receive.rs similarity index 100% rename from src/subcommand/wallet/receive.rs rename to src/wallet/receive.rs diff --git a/src/subcommand/wallet/restore.rs b/src/wallet/restore.rs similarity index 100% rename from src/subcommand/wallet/restore.rs rename to src/wallet/restore.rs diff --git a/src/subcommand/wallet/sats.rs b/src/wallet/sats.rs similarity index 100% rename from src/subcommand/wallet/sats.rs rename to src/wallet/sats.rs diff --git a/src/subcommand/wallet/send.rs b/src/wallet/send.rs similarity index 98% rename from src/subcommand/wallet/send.rs rename to src/wallet/send.rs index 305aadc59e..f080636ac0 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/wallet/send.rs @@ -1,4 +1,4 @@ -use {super::*, crate::subcommand::wallet::transaction_builder::Target}; +use {super::*, wallet::transaction_builder::Target}; #[derive(Debug, Parser)] pub(crate) struct Send { diff --git a/src/subcommand/wallet/transaction_builder.rs b/src/wallet/transaction_builder.rs similarity index 100% rename from src/subcommand/wallet/transaction_builder.rs rename to src/wallet/transaction_builder.rs diff --git a/src/subcommand/wallet/transactions.rs b/src/wallet/transactions.rs similarity index 100% rename from src/subcommand/wallet/transactions.rs rename to src/wallet/transactions.rs diff --git a/tests/etch.rs b/tests/etch.rs index 982aadcdac..3336b6344e 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -1,6 +1,9 @@ use { super::*, - ord::{subcommand::wallet::etch::Output, Rune}, + ord::{ + wallet::{balance, etch::Output}, + Rune, + }, }; #[test] @@ -176,7 +179,7 @@ fn runes_can_be_etched() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.runes.unwrap()[&Rune(RUNE)], 10000); } @@ -251,7 +254,7 @@ fn etch_does_not_select_inscribed_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 5000000000); @@ -264,7 +267,7 @@ fn etch_does_not_select_inscribed_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); @@ -303,7 +306,7 @@ fn inscribe_does_not_select_runic_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 0); @@ -401,7 +404,7 @@ fn send_inscription_does_not_select_runic_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 10000); diff --git a/tests/lib.rs b/tests/lib.rs index 50f835f7f0..09e1422fcd 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -55,13 +55,13 @@ macro_rules! assert_regex_match { const RUNE: u128 = 99246114928149462; -type Inscribe = ord::subcommand::wallet::inscribe::Output; -type Etch = ord::subcommand::wallet::etch::Output; +type Inscribe = ord::wallet::inscribe::Output; +type Etch = ord::wallet::etch::Output; fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) .rpc_server(rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); } fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { diff --git a/tests/server.rs b/tests/server.rs index c7792fb279..6f0a100abb 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -1,6 +1,6 @@ use { super::*, crate::command_builder::ToArgs, ciborium::value::Integer, - ord::subcommand::wallet::send::Output, + ord::wallet::send::Output, }; #[test] diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index ea50fe36b8..34ff5917ef 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::wallet::balance::Output}; +use {super::*, ord::wallet::balance::Output}; #[test] fn wallet_balance() { diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index db966c39a8..38cb7d078d 100644 --- a/tests/wallet/cardinals.rs +++ b/tests/wallet/cardinals.rs @@ -1,6 +1,6 @@ use { super::*, - ord::subcommand::wallet::{cardinals::CardinalUtxo, outputs::Output}, + ord::wallet::{cardinals::CardinalUtxo, outputs::Output}, }; #[test] diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 59dd85525c..4bec63a8c8 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::wallet::create::Output}; +use {super::*, ord::wallet::create::Output}; #[test] fn create() { diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 0beb519640..d91fe583e9 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1,4 +1,8 @@ -use {super::*, std::ops::Deref}; +use { + super::*, + ord::wallet::{create, inscriptions, receive}, + std::ops::Deref, +}; #[test] fn inscribe_creates_inscriptions() { @@ -336,7 +340,7 @@ fn inscribe_with_wallet_named_foo() { CommandBuilder::new("wallet --name foo create") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -398,7 +402,7 @@ fn inscribe_to_specific_destination() { let destination = CommandBuilder::new("wallet receive") .rpc_server(&rpc_server) - .run_and_deserialize_output::() + .run_and_deserialize_output::() .address; let txid = CommandBuilder::new(format!( @@ -462,7 +466,7 @@ fn inscribe_works_with_postage() { let inscriptions = CommandBuilder::new("wallet inscriptions".to_string()) .write("foo.txt", [0; 350]) .rpc_server(&rpc_server) - .run_and_deserialize_output::>(); + .run_and_deserialize_output::>(); pretty_assert_eq!(inscriptions[0].postage, 5 * COIN_VALUE); } diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 4ff430510c..810a5992d7 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -1,6 +1,6 @@ use { super::*, - ord::subcommand::wallet::{inscriptions, receive, send}, + ord::wallet::{inscriptions, receive, send}, }; #[test] diff --git a/tests/wallet/outputs.rs b/tests/wallet/outputs.rs index 39a648e38e..f79552a183 100644 --- a/tests/wallet/outputs.rs +++ b/tests/wallet/outputs.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::wallet::outputs::Output}; +use {super::*, ord::wallet::outputs::Output}; #[test] fn outputs() { diff --git a/tests/wallet/receive.rs b/tests/wallet/receive.rs index 3f5f9120d4..7e2f6b35b3 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::wallet::receive::Output}; +use {super::*, ord::wallet::receive}; #[test] fn receive() { @@ -7,7 +7,7 @@ fn receive() { let output = CommandBuilder::new("wallet receive") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert!(output.address.is_valid_for_network(Network::Bitcoin)); } diff --git a/tests/wallet/restore.rs b/tests/wallet/restore.rs index f13e601a70..ae86bee6fe 100644 --- a/tests/wallet/restore.rs +++ b/tests/wallet/restore.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::wallet::create}; +use {super::*, ord::wallet::create}; #[test] fn restore_generates_same_descriptors() { diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index ca425bd665..76cd6fd38a 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -1,6 +1,6 @@ use { super::*, - ord::subcommand::wallet::sats::{OutputRare, OutputTsv}, + ord::wallet::sats::{OutputRare, OutputTsv}, }; #[test] diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index a561c4cf42..a0bb093b74 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -1,4 +1,8 @@ -use {super::*, ord::subcommand::wallet::send::Output, std::collections::BTreeMap}; +use { + super::*, + ord::wallet::{create, send, balance}, + std::collections::BTreeMap, +}; #[test] fn inscriptions_can_be_sent() { @@ -15,7 +19,7 @@ fn inscriptions_can_be_sent() { )) .rpc_server(&rpc_server) .stdout_regex(r".*") - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); let txid = rpc_server.mempool()[0].txid(); assert_eq!(txid, output.transaction); @@ -73,7 +77,7 @@ fn send_inscribed_sat() { "wallet send --fee-rate 1 bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {inscription}", )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -95,13 +99,13 @@ fn send_on_mainnnet_works_with_wallet_named_foo() { CommandBuilder::new("wallet --name foo create") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); CommandBuilder::new(format!( "wallet --name foo send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {txid}:0:0" )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); } #[test] @@ -131,7 +135,7 @@ fn send_on_mainnnet_works_with_wallet_named_ord() { "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {txid}:0:0" )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(rpc_server.mempool()[0].txid(), output.transaction); } @@ -202,7 +206,7 @@ fn can_send_after_dust_limit_from_an_inscription() { "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {output}:330" )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); } #[test] @@ -283,7 +287,7 @@ fn splitting_merged_inscriptions_is_possible() { reveal_txid, )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -293,7 +297,7 @@ fn splitting_merged_inscriptions_is_possible() { reveal_txid, )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -303,7 +307,7 @@ fn splitting_merged_inscriptions_is_possible() { reveal_txid, )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); } #[test] @@ -335,7 +339,7 @@ fn send_btc_with_fee_rate() { "wallet send --fee-rate 13.3 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc", ) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); let tx = &rpc_server.mempool()[0]; let mut fee = 0; @@ -377,7 +381,7 @@ fn send_btc_locks_inscriptions() { CommandBuilder::new("wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!( rpc_server.sent(), @@ -423,7 +427,7 @@ fn wallet_send_with_fee_rate() { "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0" )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); let tx = &rpc_server.mempool()[0]; let mut fee = 0; @@ -474,7 +478,7 @@ fn wallet_send_with_fee_rate_and_target_postage() { "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0 --postage 77000sat" )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); let tx = &rpc_server.mempool()[0]; let mut fee = 0; @@ -586,7 +590,7 @@ fn sending_rune_works() { Rune(RUNE) )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -629,7 +633,7 @@ fn sending_spaced_rune_works() { "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000Aโ€ขAAAAAAAAAAAA", ) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -686,7 +690,7 @@ fn sending_rune_with_divisibility_works() { rune )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -739,7 +743,7 @@ fn sending_rune_leaves_unspent_runes_in_wallet() { Rune(RUNE) )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -805,7 +809,7 @@ fn sending_rune_creates_transaction_with_expected_runestone() { rune, )) .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); rpc_server.mine_blocks(1); @@ -912,8 +916,8 @@ fn sending_rune_does_not_send_inscription() { assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(), - ord::subcommand::wallet::balance::Output { + .run_and_deserialize_output::(), + balance::Output { cardinal: 10000, ordinal: 10000, runic: Some(0), @@ -936,8 +940,8 @@ fn sending_rune_does_not_send_inscription() { assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(), - ord::subcommand::wallet::balance::Output { + .run_and_deserialize_output::(), + balance::Output { cardinal: 0, ordinal: 10000, runic: Some(10000), diff --git a/tests/wallet/transactions.rs b/tests/wallet/transactions.rs index 8f3fd4ffc9..353184fd13 100644 --- a/tests/wallet/transactions.rs +++ b/tests/wallet/transactions.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::wallet::transactions::Output}; +use {super::*, ord::wallet::transactions::Output}; #[test] fn transactions() { From 8be34a5bfdcd52a192706944a254864fcf972e1c Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sat, 13 Jan 2024 20:08:04 +0100 Subject: [PATCH 39/74] Place clippy --- src/wallet/inscribe.rs | 2 +- tests/server.rs | 3 +-- tests/wallet/send.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/wallet/inscribe.rs b/src/wallet/inscribe.rs index a5a6ea4d58..f3c77758ef 100644 --- a/src/wallet/inscribe.rs +++ b/src/wallet/inscribe.rs @@ -1,7 +1,6 @@ use { self::batch::{Batch, Batchfile, Mode}, super::*, - wallet::transaction_builder::Target, bitcoin::{ blockdata::{opcodes, script}, key::PrivateKey, @@ -13,6 +12,7 @@ use { taproot::{ControlBlock, LeafVersion, TapLeafHash, TaprootBuilder}, }, bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, SignRawTransactionInput, Timestamp}, + wallet::transaction_builder::Target, }; mod batch; diff --git a/tests/server.rs b/tests/server.rs index 6f0a100abb..133932b6ce 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -1,6 +1,5 @@ use { - super::*, crate::command_builder::ToArgs, ciborium::value::Integer, - ord::wallet::send::Output, + super::*, crate::command_builder::ToArgs, ciborium::value::Integer, ord::wallet::send::Output, }; #[test] diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index a0bb093b74..40db4342ec 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -1,6 +1,6 @@ use { super::*, - ord::wallet::{create, send, balance}, + ord::wallet::{balance, create, send}, std::collections::BTreeMap, }; From 288d25db2b18b29f0416f4f799774c2dd6cabd88 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 15 Jan 2024 21:37:31 +0100 Subject: [PATCH 40/74] Horrible state --- src/subcommand.rs | 1 + src/wallet.rs | 158 +--- src/wallet/balance.rs | 70 -- src/wallet/cardinals.rs | 33 - src/wallet/create.rs | 33 - src/wallet/etch.rs | 131 ---- src/wallet/inscribe.rs | 1356 +--------------------------------- src/wallet/inscribe/batch.rs | 26 +- src/wallet/inscriptions.rs | 37 - src/wallet/outputs.rs | 19 - src/wallet/receive.rs | 14 - src/wallet/restore.rs | 21 - src/wallet/sats.rs | 321 -------- src/wallet/send.rs | 261 ------- src/wallet/transactions.rs | 34 - 15 files changed, 55 insertions(+), 2460 deletions(-) delete mode 100644 src/wallet/balance.rs delete mode 100644 src/wallet/cardinals.rs delete mode 100644 src/wallet/create.rs delete mode 100644 src/wallet/etch.rs delete mode 100644 src/wallet/inscriptions.rs delete mode 100644 src/wallet/outputs.rs delete mode 100644 src/wallet/receive.rs delete mode 100644 src/wallet/restore.rs delete mode 100644 src/wallet/sats.rs delete mode 100644 src/wallet/send.rs delete mode 100644 src/wallet/transactions.rs diff --git a/src/subcommand.rs b/src/subcommand.rs index 72742ae3dd..59a429c0a9 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -14,6 +14,7 @@ pub mod subsidy; pub mod supply; pub mod teleburn; pub mod traits; +mod wallet; #[derive(Debug, Parser)] pub(crate) enum Subcommand { diff --git a/src/wallet.rs b/src/wallet.rs index 4221fc5c51..15ac95e5e8 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,9 +1,6 @@ use { super::*, - bitcoin::secp256k1::{ - rand::{self, RngCore}, - All, Secp256k1, - }, + bitcoin::secp256k1::{All, Secp256k1}, bitcoin::{ bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}, Network, @@ -11,133 +8,14 @@ use { bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp}, fee_rate::FeeRate, http::StatusCode, + inscribe::ParentInfo, miniscript::descriptor::{Descriptor, DescriptorSecretKey, DescriptorXKey, Wildcard}, reqwest::{header, Url}, transaction_builder::TransactionBuilder, }; -pub mod balance; -pub mod cardinals; -pub mod create; -pub mod etch; pub mod inscribe; -pub mod inscriptions; -pub mod outputs; -pub mod receive; -pub mod restore; -pub mod sats; -pub mod send; pub mod transaction_builder; -pub mod transactions; - -#[derive(Debug, Parser)] -pub(crate) struct WalletCommand { - #[arg(long, default_value = "ord", help = "Use wallet named .")] - pub(crate) name: String, - #[arg(long, alias = "nosync", help = "Do not update index.")] - pub(crate) no_sync: bool, - #[command(subcommand)] - pub(crate) subcommand: Subcommand, -} - -#[derive(Debug, Parser)] -pub(crate) enum Subcommand { - #[command(about = "Get wallet balance")] - Balance, - #[command(about = "Create new wallet")] - Create(create::Create), - #[command(about = "Create rune")] - Etch(etch::Etch), - #[command(about = "Create inscription")] - Inscribe(inscribe::Inscribe), - #[command(about = "List wallet inscriptions")] - Inscriptions, - #[command(about = "Generate receive address")] - Receive, - #[command(about = "Restore wallet")] - Restore(restore::Restore), - #[command(about = "List wallet satoshis")] - Sats(sats::Sats), - #[command(about = "Send sat or inscription")] - Send(send::Send), - #[command(about = "See wallet transactions")] - Transactions(transactions::Transactions), - #[command(about = "List all unspent outputs in wallet")] - Outputs, - #[command(about = "List unspent cardinal outputs in wallet")] - Cardinals, -} - -impl WalletCommand { - pub(crate) fn run(self, options: Options) -> SubcommandResult { - let index = Arc::new(Index::open(&options)?); - let handle = axum_server::Handle::new(); - LISTENERS.lock().unwrap().push(handle.clone()); - - let ord_url: Url = { - format!( - "http://127.0.0.1:{}", - TcpListener::bind("127.0.0.1:0")?.local_addr()?.port() // very hacky - ) - .parse() - .unwrap() - }; - - { - let options = options.clone(); - let ord_url = ord_url.clone(); - std::thread::spawn(move || { - crate::subcommand::server::Server { - address: ord_url.host_str().map(|a| a.to_string()), - acme_domain: vec![], - csp_origin: None, - http_port: ord_url.port(), - https_port: None, - acme_cache: None, - acme_contact: vec![], - http: true, - https: false, - redirect_http_to_https: false, - enable_json_api: true, - decompress: false, - no_sync: self.no_sync, - } - .run(options, index, handle) - .unwrap() - }); - } - - let wallet = Wallet { - no_sync: self.no_sync, - options, - ord_url, - name: self.name.clone(), - }; - - let result = match self.subcommand { - Subcommand::Balance => balance::run(wallet), - Subcommand::Create(create) => create.run(wallet), - Subcommand::Etch(etch) => etch.run(wallet), - Subcommand::Inscribe(inscribe) => inscribe.run(wallet), - Subcommand::Inscriptions => inscriptions::run(wallet), - Subcommand::Receive => receive::run(wallet), - Subcommand::Restore(restore) => restore.run(wallet), - Subcommand::Sats(sats) => sats.run(wallet), - Subcommand::Send(send) => send.run(wallet), - Subcommand::Transactions(transactions) => transactions.run(wallet), - Subcommand::Outputs => outputs::run(wallet), - Subcommand::Cardinals => cardinals::run(wallet), - }; - - LISTENERS - .lock() - .unwrap() - .iter() - .for_each(|handle| handle.shutdown()); - - result - } -} pub(crate) struct Wallet { pub(crate) name: String, @@ -417,6 +295,38 @@ impl Wallet { ) } + pub(crate) fn get_parent_info( + &self, + parent: Option, + utxos: &BTreeMap, + ) -> Result> { + if let Some(parent_id) = parent { + let satpoint = self + .get_inscription_satpoint(parent_id) + .map_err(|_| anyhow!(format!("parent {parent_id} does not exist")))?; + + if !utxos.contains_key(&satpoint.outpoint) { + return Err(anyhow!(format!("parent {parent_id} not in wallet"))); + } + + Ok(Some(ParentInfo { + destination: self.get_change_address()?, + id: parent_id, + location: satpoint, + tx_out: self + .bitcoin_client()? + .get_raw_transaction(&satpoint.outpoint.txid, None) + .expect("parent transaction not found in index") + .output + .into_iter() + .nth(satpoint.outpoint.vout.try_into().unwrap()) + .expect("current transaction output"), + })) + } else { + Ok(None) + } + } + pub(crate) fn get_change_address(&self) -> Result
{ Ok( self diff --git a/src/wallet/balance.rs b/src/wallet/balance.rs deleted file mode 100644 index 5d68d8a0b3..0000000000 --- a/src/wallet/balance.rs +++ /dev/null @@ -1,70 +0,0 @@ -use {super::*, std::collections::BTreeSet}; - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Output { - pub cardinal: u64, - pub ordinal: u64, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub runes: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub runic: Option, - pub total: u64, -} - -pub(crate) fn run(wallet: Wallet) -> SubcommandResult { - let unspent_outputs = wallet.get_unspent_outputs()?; - - let inscription_outputs = wallet - .get_inscriptions()? - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - let mut cardinal = 0; - let mut ordinal = 0; - let mut runes = BTreeMap::new(); - let mut runic = 0; - - for (output, amount) in unspent_outputs { - let rune_balances = wallet.get_runes_balances_for_output(&output)?; - - if inscription_outputs.contains(&output) { - ordinal += amount.to_sat(); - } else if !rune_balances.is_empty() { - for (spaced_rune, pile) in rune_balances { - *runes.entry(spaced_rune.rune).or_default() += pile.amount; - } - runic += amount.to_sat(); - } else { - cardinal += amount.to_sat(); - } - } - - Ok(Some(Box::new(Output { - cardinal, - ordinal, - runes: wallet.check_rune_index()?.then_some(runes), - runic: wallet.check_rune_index()?.then_some(runic), - total: cardinal + ordinal + runic, - }))) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn runes_and_runic_fields_are_not_present_if_none() { - assert_eq!( - serde_json::to_string(&Output { - cardinal: 0, - ordinal: 0, - runes: None, - runic: None, - total: 0 - }) - .unwrap(), - r#"{"cardinal":0,"ordinal":0,"total":0}"# - ); - } -} diff --git a/src/wallet/cardinals.rs b/src/wallet/cardinals.rs deleted file mode 100644 index a7389a4b54..0000000000 --- a/src/wallet/cardinals.rs +++ /dev/null @@ -1,33 +0,0 @@ -use {super::*, std::collections::BTreeSet}; - -#[derive(Serialize, Deserialize)] -pub struct CardinalUtxo { - pub output: OutPoint, - pub amount: u64, -} - -pub(crate) fn run(wallet: Wallet) -> SubcommandResult { - let unspent_outputs = wallet.get_unspent_outputs()?; - - let inscribed_utxos = wallet - .get_inscriptions()? - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - let cardinal_utxos = unspent_outputs - .iter() - .filter_map(|(output, amount)| { - if inscribed_utxos.contains(output) { - None - } else { - Some(CardinalUtxo { - output: *output, - amount: amount.to_sat(), - }) - } - }) - .collect::>(); - - Ok(Some(Box::new(cardinal_utxos))) -} diff --git a/src/wallet/create.rs b/src/wallet/create.rs deleted file mode 100644 index 45a8d6065b..0000000000 --- a/src/wallet/create.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -#[derive(Serialize, Deserialize)] -pub struct Output { - pub mnemonic: Mnemonic, - pub passphrase: Option, -} - -#[derive(Debug, Parser)] -pub(crate) struct Create { - #[arg( - long, - default_value = "", - help = "Use to derive wallet seed." - )] - pub(crate) passphrase: String, -} - -impl Create { - pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - let mut entropy = [0; 16]; - rand::thread_rng().fill_bytes(&mut entropy); - - let mnemonic = Mnemonic::from_entropy(&entropy)?; - - wallet.initialize(mnemonic.to_seed(self.passphrase.clone()))?; - - Ok(Some(Box::new(Output { - mnemonic, - passphrase: Some(self.passphrase), - }))) - } -} diff --git a/src/wallet/etch.rs b/src/wallet/etch.rs deleted file mode 100644 index e290426ca6..0000000000 --- a/src/wallet/etch.rs +++ /dev/null @@ -1,131 +0,0 @@ -use super::*; - -#[derive(Debug, Parser)] -pub(crate) struct Etch { - #[clap(long, help = "Set divisibility to .")] - divisibility: u8, - #[clap(long, help = "Etch with fee rate of sats/vB.")] - fee_rate: FeeRate, - #[clap(long, help = "Etch rune . May contain `.` or `โ€ข`as spacers.")] - rune: SpacedRune, - #[clap(long, help = "Set supply to .")] - supply: Decimal, - #[clap(long, help = "Set currency symbol to .")] - symbol: char, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Output { - pub rune: SpacedRune, - pub transaction: Txid, -} - -impl Etch { - pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - ensure!( - wallet.check_rune_index()?, - "`ord wallet etch` requires index created with `--index-runes` flag", - ); - - let SpacedRune { rune, spacers } = self.rune; - - let count = wallet.bitcoin_client()?.get_block_count()?; - - ensure!( - wallet.get_rune(rune)?.is_none(), - "rune `{}` has already been etched", - rune, - ); - - let minimum_at_height = - Rune::minimum_at_height(wallet.chain(), Height(u32::try_from(count).unwrap() + 1)); - - ensure!( - rune >= minimum_at_height, - "rune is less than minimum for next block: {} < {minimum_at_height}", - rune, - ); - - ensure!(!rune.is_reserved(), "rune `{}` is reserved", rune); - - ensure!( - self.divisibility <= crate::runes::MAX_DIVISIBILITY, - " must be equal to or less than 38" - ); - - let destination = wallet.get_change_address()?; - - let runestone = Runestone { - etching: Some(Etching { - deadline: None, - divisibility: self.divisibility, - limit: None, - rune: Some(rune), - spacers, - symbol: Some(self.symbol), - term: None, - }), - edicts: vec![Edict { - amount: self.supply.to_amount(self.divisibility)?, - id: 0, - output: 1, - }], - default_output: None, - burn: false, - }; - - let script_pubkey = runestone.encipher(); - - ensure!( - script_pubkey.len() <= 82, - "runestone greater than maximum OP_RETURN size: {} > 82", - script_pubkey.len() - ); - - let unfunded_transaction = Transaction { - version: 2, - lock_time: LockTime::ZERO, - input: Vec::new(), - output: vec![ - TxOut { - script_pubkey, - value: 0, - }, - TxOut { - script_pubkey: destination.script_pubkey(), - value: TARGET_POSTAGE.to_sat(), - }, - ], - }; - - let inscriptions = wallet - .get_inscriptions()? - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - if !wallet.bitcoin_client()?.lock_unspent(&inscriptions)? { - bail!("failed to lock UTXOs"); - } - - let unsigned_transaction = fund_raw_transaction( - &wallet.bitcoin_client()?, - self.fee_rate, - &unfunded_transaction, - )?; - - let signed_transaction = wallet - .bitcoin_client()? - .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? - .hex; - - let transaction = wallet - .bitcoin_client()? - .send_raw_transaction(&signed_transaction)?; - - Ok(Some(Box::new(Output { - rune: self.rune, - transaction, - }))) - } -} diff --git a/src/wallet/inscribe.rs b/src/wallet/inscribe.rs index f3c77758ef..b8dfd2c0b2 100644 --- a/src/wallet/inscribe.rs +++ b/src/wallet/inscribe.rs @@ -1,5 +1,5 @@ use { - self::batch::{Batch, Batchfile, Mode}, + // self::batch::{Batch, Batchfile, Mode}, super::*, bitcoin::{ blockdata::{opcodes, script}, @@ -15,7 +15,7 @@ use { wallet::transaction_builder::Target, }; -mod batch; +pub mod batch; #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct InscriptionInfo { @@ -33,1351 +33,9 @@ pub struct Output { } #[derive(Clone, Debug)] -pub(crate) struct ParentInfo { - destination: Address, - id: InscriptionId, - location: SatPoint, - tx_out: TxOut, -} - -#[derive(Debug, Parser)] -#[clap( - group = ArgGroup::new("source") - .required(true) - .args(&["file", "batch"]), -)] -pub(crate) struct Inscribe { - #[arg( - long, - help = "Inscribe multiple inscriptions defined in a yaml .", - conflicts_with_all = &[ - "cbor_metadata", "destination", "file", "json_metadata", "metaprotocol", "parent", "postage", "reinscribe", "satpoint" - ] - )] - pub(crate) batch: Option, - #[arg( - long, - help = "Include CBOR in file at as inscription metadata", - conflicts_with = "json_metadata" - )] - pub(crate) cbor_metadata: Option, - #[arg( - long, - help = "Use sats/vbyte for commit transaction.\nDefaults to if unset." - )] - pub(crate) commit_fee_rate: Option, - #[arg(long, help = "Compress inscription content with brotli.")] - pub(crate) compress: bool, - #[arg(long, help = "Send inscription to .")] - pub(crate) destination: Option>, - #[arg(long, help = "Don't sign or broadcast transactions.")] - pub(crate) dry_run: bool, - #[arg(long, help = "Use fee rate of sats/vB.")] - pub(crate) fee_rate: FeeRate, - #[arg(long, help = "Inscribe sat with contents of .")] - pub(crate) file: Option, - #[arg( - long, - help = "Include JSON in file at converted to CBOR as inscription metadata", - conflicts_with = "cbor_metadata" - )] - pub(crate) json_metadata: Option, - #[clap(long, help = "Set inscription metaprotocol to .")] - pub(crate) metaprotocol: Option, - #[arg(long, alias = "nobackup", help = "Do not back up recovery key.")] - pub(crate) no_backup: bool, - #[arg( - long, - alias = "nolimit", - help = "Do not check that transactions are equal to or below the MAX_STANDARD_TX_WEIGHT of 400,000 weight units. Transactions over this limit are currently nonstandard and will not be relayed by bitcoind in its default configuration. Do not use this flag unless you understand the implications." - )] - pub(crate) no_limit: bool, - #[clap(long, help = "Make inscription a child of .")] - pub(crate) parent: Option, - #[arg( - long, - help = "Amount of postage to include in the inscription. Default `10000sat`." - )] - pub(crate) postage: Option, - #[clap(long, help = "Allow reinscription.")] - pub(crate) reinscribe: bool, - #[arg(long, help = "Inscribe .")] - pub(crate) satpoint: Option, - #[arg(long, help = "Inscribe .", conflicts_with = "satpoint")] - pub(crate) sat: Option, -} - -impl Inscribe { - pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - let metadata = Inscribe::parse_metadata(self.cbor_metadata, self.json_metadata)?; - - let utxos = wallet.get_unspent_outputs()?; - - let locked_utxos = wallet.get_locked_outputs()?; - - let runic_utxos = wallet.get_runic_outputs()?; - - let chain = wallet.chain(); - - let postage; - let destinations; - let inscriptions; - let mode; - let parent_info; - let sat; - - match (self.file, self.batch) { - (Some(file), None) => { - parent_info = Inscribe::get_parent_info(self.parent, &utxos, &wallet)?; - - postage = self.postage.unwrap_or(TARGET_POSTAGE); - - inscriptions = vec![Inscription::from_file( - chain, - file, - self.parent, - None, - self.metaprotocol, - metadata, - self.compress, - )?]; - - mode = Mode::SeparateOutputs; - - sat = self.sat; - - destinations = vec![match self.destination.clone() { - Some(destination) => destination.require_network(chain.network())?, - None => wallet.get_change_address()?, - }]; - } - (None, Some(batch)) => { - let batchfile = Batchfile::load(&batch)?; - - parent_info = Inscribe::get_parent_info(batchfile.parent, &utxos, &wallet)?; - - postage = batchfile - .postage - .map(Amount::from_sat) - .unwrap_or(TARGET_POSTAGE); - - (inscriptions, destinations) = batchfile.inscriptions( - &wallet, - parent_info.as_ref().map(|info| info.tx_out.value), - metadata, - postage, - self.compress, - )?; - - mode = batchfile.mode; - - if batchfile.sat.is_some() && mode != Mode::SameSat { - return Err(anyhow!("`sat` can only be set in `same-sat` mode")); - } - - sat = batchfile.sat; - } - _ => unreachable!(), - } - - let satpoint = if let Some(sat) = sat { - Some(wallet.find_sat_in_outputs(sat, &utxos)?) - } else { - self.satpoint - }; - - Batch { - commit_fee_rate: self.commit_fee_rate.unwrap_or(self.fee_rate), - destinations, - dry_run: self.dry_run, - inscriptions, - mode, - no_backup: self.no_backup, - no_limit: self.no_limit, - parent_info, - postage, - reinscribe: self.reinscribe, - reveal_fee_rate: self.fee_rate, - satpoint, - } - .inscribe(&locked_utxos, runic_utxos, &utxos, &wallet) - } - - fn parse_metadata(cbor: Option, json: Option) -> Result>> { - if let Some(path) = cbor { - let cbor = fs::read(path)?; - let _value: Value = ciborium::from_reader(Cursor::new(cbor.clone())) - .context("failed to parse CBOR metadata")?; - - Ok(Some(cbor)) - } else if let Some(path) = json { - let value: serde_json::Value = - serde_json::from_reader(File::open(path)?).context("failed to parse JSON metadata")?; - let mut cbor = Vec::new(); - ciborium::into_writer(&value, &mut cbor)?; - - Ok(Some(cbor)) - } else { - Ok(None) - } - } - - fn get_parent_info( - parent: Option, - utxos: &BTreeMap, - wallet: &Wallet, - ) -> Result> { - if let Some(parent_id) = parent { - let satpoint = wallet - .get_inscription_satpoint(parent_id) - .map_err(|_| anyhow!(format!("parent {parent_id} does not exist")))?; - - if !utxos.contains_key(&satpoint.outpoint) { - return Err(anyhow!(format!("parent {parent_id} not in wallet"))); - } - - Ok(Some(ParentInfo { - destination: wallet.get_change_address()?, - id: parent_id, - location: satpoint, - tx_out: wallet - .bitcoin_client()? - .get_raw_transaction(&satpoint.outpoint.txid, None) - .expect("parent transaction not found in index") - .output - .into_iter() - .nth(satpoint.outpoint.vout.try_into().unwrap()) - .expect("current transaction output"), - })) - } else { - Ok(None) - } - } -} - -#[cfg(test)] -mod tests { - use { - self::batch::BatchEntry, - super::*, - serde_yaml::{Mapping, Value}, - }; - - #[test] - fn reveal_transaction_pays_fee() { - let utxos = vec![(outpoint(1), Amount::from_sat(20000))]; - let inscription = inscription("text/plain", "ord"); - let commit_address = change(0); - let reveal_address = recipient(); - let change = [commit_address, change(1)]; - - let (commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint: Some(satpoint(1, 0)), - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(1.0).unwrap(), - reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - BTreeMap::new(), - Chain::Mainnet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - change, - ) - .unwrap(); - - #[allow(clippy::cast_possible_truncation)] - #[allow(clippy::cast_sign_loss)] - let fee = Amount::from_sat((1.0 * (reveal_tx.vsize() as f64)).ceil() as u64); - - assert_eq!( - reveal_tx.output[0].value, - 20000 - fee.to_sat() - (20000 - commit_tx.output[0].value), - ); - } - - #[test] - fn inscribe_transactions_opt_in_to_rbf() { - let utxos = vec![(outpoint(1), Amount::from_sat(20000))]; - let inscription = inscription("text/plain", "ord"); - let commit_address = change(0); - let reveal_address = recipient(); - let change = [commit_address, change(1)]; - - let (commit_tx, reveal_tx, _, _) = Batch { - satpoint: Some(satpoint(1, 0)), - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(1.0).unwrap(), - reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - BTreeMap::new(), - Chain::Mainnet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - change, - ) - .unwrap(); - - assert!(commit_tx.is_explicitly_rbf()); - assert!(reveal_tx.is_explicitly_rbf()); - } - - #[test] - fn inscribe_with_no_satpoint_and_no_cardinal_utxos() { - let utxos = vec![(outpoint(1), Amount::from_sat(1000))]; - let mut inscriptions = BTreeMap::new(); - inscriptions.insert( - SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - inscription_id(1), - ); - - let inscription = inscription("text/plain", "ord"); - let satpoint = None; - let commit_address = change(0); - let reveal_address = recipient(); - - let error = Batch { - satpoint, - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(1.0).unwrap(), - reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - inscriptions, - Chain::Mainnet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(1)], - ) - .unwrap_err() - .to_string(); - - assert!( - error.contains("wallet contains no cardinal utxos"), - "{}", - error - ); - } - - #[test] - fn inscribe_with_no_satpoint_and_enough_cardinal_utxos() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(20_000)), - (outpoint(2), Amount::from_sat(20_000)), - ]; - let mut inscriptions = BTreeMap::new(); - inscriptions.insert( - SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - inscription_id(1), - ); - - let inscription = inscription("text/plain", "ord"); - let satpoint = None; - let commit_address = change(0); - let reveal_address = recipient(); - - assert!(Batch { - satpoint, - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(1.0).unwrap(), - reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - inscriptions, - Chain::Mainnet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(1)], - ) - .is_ok()) - } - - #[test] - fn inscribe_with_custom_fee_rate() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(20_000)), - ]; - let mut inscriptions = BTreeMap::new(); - inscriptions.insert( - SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - inscription_id(1), - ); - - let inscription = inscription("text/plain", "ord"); - let satpoint = None; - let commit_address = change(0); - let reveal_address = recipient(); - let fee_rate = 3.3; - - let (commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint, - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(fee_rate).unwrap(), - reveal_fee_rate: FeeRate::try_from(fee_rate).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(1)], - ) - .unwrap(); - - let sig_vbytes = 17; - let fee = FeeRate::try_from(fee_rate) - .unwrap() - .fee(commit_tx.vsize() + sig_vbytes) - .to_sat(); - - let reveal_value = commit_tx - .output - .iter() - .map(|o| o.value) - .reduce(|acc, i| acc + i) - .unwrap(); - - assert_eq!(reveal_value, 20_000 - fee); - - let fee = FeeRate::try_from(fee_rate) - .unwrap() - .fee(reveal_tx.vsize()) - .to_sat(); - - assert_eq!( - reveal_tx.output[0].value, - 20_000 - fee - (20_000 - commit_tx.output[0].value), - ); - } - - #[test] - fn inscribe_with_parent() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(20_000)), - ]; - - let mut inscriptions = BTreeMap::new(); - let parent_inscription = inscription_id(1); - let parent_info = ParentInfo { - destination: change(3), - id: parent_inscription, - location: SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - tx_out: TxOut { - script_pubkey: change(0).script_pubkey(), - value: 10000, - }, - }; - - inscriptions.insert(parent_info.location, parent_inscription); - - let child_inscription = InscriptionTemplate { - parent: Some(parent_inscription), - ..Default::default() - } - .into(); - - let commit_address = change(1); - let reveal_address = recipient(); - let fee_rate = 4.0; - - let (commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint: None, - parent_info: Some(parent_info.clone()), - inscriptions: vec![child_inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(fee_rate).unwrap(), - reveal_fee_rate: FeeRate::try_from(fee_rate).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ) - .unwrap(); - - let sig_vbytes = 17; - let fee = FeeRate::try_from(fee_rate) - .unwrap() - .fee(commit_tx.vsize() + sig_vbytes) - .to_sat(); - - let reveal_value = commit_tx - .output - .iter() - .map(|o| o.value) - .reduce(|acc, i| acc + i) - .unwrap(); - - assert_eq!(reveal_value, 20_000 - fee); - - let sig_vbytes = 16; - let fee = FeeRate::try_from(fee_rate) - .unwrap() - .fee(reveal_tx.vsize() + sig_vbytes) - .to_sat(); - - assert_eq!(fee, commit_tx.output[0].value - reveal_tx.output[1].value,); - assert_eq!( - reveal_tx.output[0].script_pubkey, - parent_info.destination.script_pubkey() - ); - assert_eq!(reveal_tx.output[0].value, parent_info.tx_out.value); - pretty_assert_eq!( - reveal_tx.input[0], - TxIn { - previous_output: parent_info.location.outpoint, - sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, - ..Default::default() - } - ); - } - - #[test] - fn inscribe_with_commit_fee_rate() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(20_000)), - ]; - let mut inscriptions = BTreeMap::new(); - inscriptions.insert( - SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - inscription_id(1), - ); - - let inscription = inscription("text/plain", "ord"); - let satpoint = None; - let commit_address = change(0); - let reveal_address = recipient(); - let commit_fee_rate = 3.3; - let fee_rate = 1.0; - - let (commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint, - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(commit_fee_rate).unwrap(), - reveal_fee_rate: FeeRate::try_from(fee_rate).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(1)], - ) - .unwrap(); - - let sig_vbytes = 17; - let fee = FeeRate::try_from(commit_fee_rate) - .unwrap() - .fee(commit_tx.vsize() + sig_vbytes) - .to_sat(); - - let reveal_value = commit_tx - .output - .iter() - .map(|o| o.value) - .reduce(|acc, i| acc + i) - .unwrap(); - - assert_eq!(reveal_value, 20_000 - fee); - - let fee = FeeRate::try_from(fee_rate) - .unwrap() - .fee(reveal_tx.vsize()) - .to_sat(); - - assert_eq!( - reveal_tx.output[0].value, - 20_000 - fee - (20_000 - commit_tx.output[0].value), - ); - } - - #[test] - fn inscribe_over_max_standard_tx_weight() { - let utxos = vec![(outpoint(1), Amount::from_sat(50 * COIN_VALUE))]; - - let inscription = inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize]); - let satpoint = None; - let commit_address = change(0); - let reveal_address = recipient(); - - let error = Batch { - satpoint, - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(1.0).unwrap(), - reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), - no_limit: false, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - BTreeMap::new(), - Chain::Mainnet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(1)], - ) - .unwrap_err() - .to_string(); - - assert!( - error.contains(&format!("reveal transaction weight greater than {MAX_STANDARD_TX_WEIGHT} (MAX_STANDARD_TX_WEIGHT): 402799")), - "{}", - error - ); - } - - #[test] - fn inscribe_with_no_max_standard_tx_weight() { - let utxos = vec![(outpoint(1), Amount::from_sat(50 * COIN_VALUE))]; - - let inscription = inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize]); - let satpoint = None; - let commit_address = change(0); - let reveal_address = recipient(); - - let (_commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint, - parent_info: None, - inscriptions: vec![inscription], - destinations: vec![reveal_address], - commit_fee_rate: FeeRate::try_from(1.0).unwrap(), - reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), - no_limit: true, - reinscribe: false, - postage: TARGET_POSTAGE, - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - BTreeMap::new(), - Chain::Mainnet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(1)], - ) - .unwrap(); - - assert!(reveal_tx.size() >= MAX_STANDARD_TX_WEIGHT as usize); - } - - #[test] - fn cbor_and_json_metadata_flags_conflict() { - assert_regex_match!( - Arguments::try_parse_from([ - "ord", - "wallet", - "inscribe", - "--cbor-metadata", - "foo", - "--json-metadata", - "bar", - "--file", - "baz", - ]) - .unwrap_err() - .to_string(), - ".*--cbor-metadata.*cannot be used with.*--json-metadata.*" - ); - } - - #[test] - fn batch_is_loaded_from_yaml_file() { - let parent = "8d363b28528b0cb86b5fd48615493fb175bdf132d2a3d20b4251bba3f130a5abi0" - .parse::() - .unwrap(); - - let tempdir = TempDir::new().unwrap(); - - let inscription_path = tempdir.path().join("tulip.txt"); - fs::write(&inscription_path, "tulips are pretty").unwrap(); - - let brc20_path = tempdir.path().join("token.json"); - - let batch_path = tempdir.path().join("batch.yaml"); - fs::write( - &batch_path, - format!( - "mode: separate-outputs -parent: {parent} -inscriptions: -- file: {} - metadata: - title: Lorem Ipsum - description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tristique, massa nec condimentum venenatis, ante massa tempor velit, et accumsan ipsum ligula a massa. Nunc quis orci ante. -- file: {} - metaprotocol: brc-20 -", - inscription_path.display(), - brc20_path.display() - ), - ) - .unwrap(); - - let mut metadata = Mapping::new(); - metadata.insert( - Value::String("title".to_string()), - Value::String("Lorem Ipsum".to_string()), - ); - metadata.insert(Value::String("description".to_string()), Value::String("Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tristique, massa nec condimentum venenatis, ante massa tempor velit, et accumsan ipsum ligula a massa. Nunc quis orci ante.".to_string())); - - assert_eq!( - Batchfile::load(&batch_path).unwrap(), - Batchfile { - inscriptions: vec![ - BatchEntry { - file: inscription_path, - metadata: Some(Value::Mapping(metadata)), - ..Default::default() - }, - BatchEntry { - file: brc20_path, - metaprotocol: Some("brc-20".to_string()), - ..Default::default() - } - ], - parent: Some(parent), - ..Default::default() - } - ); - } - - #[test] - fn batch_with_unknown_field_throws_error() { - let tempdir = TempDir::new().unwrap(); - let batch_path = tempdir.path().join("batch.yaml"); - fs::write( - &batch_path, - "mode: shared-output\ninscriptions:\n- file: meow.wav\nunknown: 1.)what", - ) - .unwrap(); - - assert!(Batchfile::load(&batch_path) - .unwrap_err() - .to_string() - .contains("unknown field `unknown`")); - } - - #[test] - fn batch_inscribe_with_parent() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(50_000)), - ]; - - let parent = inscription_id(1); - - let parent_info = ParentInfo { - destination: change(3), - id: parent, - location: SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - tx_out: TxOut { - script_pubkey: change(0).script_pubkey(), - value: 10000, - }, - }; - - let mut wallet_inscriptions = BTreeMap::new(); - wallet_inscriptions.insert(parent_info.location, parent); - - let commit_address = change(1); - let reveal_addresses = vec![recipient()]; - - let inscriptions = vec![ - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - ]; - - let mode = Mode::SharedOutput; - - let fee_rate = 4.0.try_into().unwrap(); - - let (commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint: None, - parent_info: Some(parent_info.clone()), - inscriptions, - destinations: reveal_addresses, - commit_fee_rate: fee_rate, - reveal_fee_rate: fee_rate, - no_limit: false, - reinscribe: false, - postage: Amount::from_sat(10_000), - mode, - ..Default::default() - } - .create_batch_inscription_transactions( - wallet_inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ) - .unwrap(); - - let sig_vbytes = 17; - let fee = fee_rate.fee(commit_tx.vsize() + sig_vbytes).to_sat(); - - let reveal_value = commit_tx - .output - .iter() - .map(|o| o.value) - .reduce(|acc, i| acc + i) - .unwrap(); - - assert_eq!(reveal_value, 50_000 - fee); - - let sig_vbytes = 16; - let fee = fee_rate.fee(reveal_tx.vsize() + sig_vbytes).to_sat(); - - assert_eq!(fee, commit_tx.output[0].value - reveal_tx.output[1].value,); - assert_eq!( - reveal_tx.output[0].script_pubkey, - parent_info.destination.script_pubkey() - ); - assert_eq!(reveal_tx.output[0].value, parent_info.tx_out.value); - pretty_assert_eq!( - reveal_tx.input[0], - TxIn { - previous_output: parent_info.location.outpoint, - sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, - ..Default::default() - } - ); - } - - #[test] - fn batch_inscribe_with_parent_not_enough_cardinals_utxos_fails() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(20_000)), - ]; - - let parent = inscription_id(1); - - let parent_info = ParentInfo { - destination: change(3), - id: parent, - location: SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - tx_out: TxOut { - script_pubkey: change(0).script_pubkey(), - value: 10000, - }, - }; - - let mut wallet_inscriptions = BTreeMap::new(); - wallet_inscriptions.insert(parent_info.location, parent); - - let inscriptions = vec![ - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - ]; - - let commit_address = change(1); - let reveal_addresses = vec![recipient()]; - - let error = Batch { - satpoint: None, - parent_info: Some(parent_info.clone()), - inscriptions, - destinations: reveal_addresses, - commit_fee_rate: 4.0.try_into().unwrap(), - reveal_fee_rate: 4.0.try_into().unwrap(), - no_limit: false, - reinscribe: false, - postage: Amount::from_sat(10_000), - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - wallet_inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ) - .unwrap_err() - .to_string(); - - assert!(error.contains( - "wallet does not contain enough cardinal UTXOs, please add additional funds to wallet." - )); - } - - #[test] - #[should_panic( - expected = "invariant: destination addresses and number of inscriptions doesn't match" - )] - fn batch_inscribe_with_inconsistent_reveal_addresses_panics() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(80_000)), - ]; - - let parent = inscription_id(1); - - let parent_info = ParentInfo { - destination: change(3), - id: parent, - location: SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - tx_out: TxOut { - script_pubkey: change(0).script_pubkey(), - value: 10000, - }, - }; - - let mut wallet_inscriptions = BTreeMap::new(); - wallet_inscriptions.insert(parent_info.location, parent); - - let inscriptions = vec![ - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - ]; - - let commit_address = change(1); - let reveal_addresses = vec![recipient(), recipient()]; - - let _ = Batch { - satpoint: None, - parent_info: Some(parent_info.clone()), - inscriptions, - destinations: reveal_addresses, - commit_fee_rate: 4.0.try_into().unwrap(), - reveal_fee_rate: 4.0.try_into().unwrap(), - no_limit: false, - reinscribe: false, - postage: Amount::from_sat(10_000), - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - wallet_inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ); - } - - #[test] - fn batch_inscribe_over_max_standard_tx_weight() { - let utxos = vec![(outpoint(1), Amount::from_sat(50 * COIN_VALUE))]; - - let wallet_inscriptions = BTreeMap::new(); - - let inscriptions = vec![ - inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize / 3]), - inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize / 3]), - inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize / 3]), - ]; - - let commit_address = change(1); - let reveal_addresses = vec![recipient()]; - - let error = Batch { - satpoint: None, - parent_info: None, - inscriptions, - destinations: reveal_addresses, - commit_fee_rate: 1.0.try_into().unwrap(), - reveal_fee_rate: 1.0.try_into().unwrap(), - no_limit: false, - reinscribe: false, - postage: Amount::from_sat(30_000), - mode: Mode::SharedOutput, - ..Default::default() - } - .create_batch_inscription_transactions( - wallet_inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ) - .unwrap_err() - .to_string(); - - assert!( - error.contains(&format!("reveal transaction weight greater than {MAX_STANDARD_TX_WEIGHT} (MAX_STANDARD_TX_WEIGHT): 402841")), - "{}", - error - ); - } - - #[test] - fn batch_inscribe_into_separate_outputs() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(80_000)), - ]; - - let wallet_inscriptions = BTreeMap::new(); - - let commit_address = change(1); - let reveal_addresses = vec![recipient(), recipient(), recipient()]; - - let inscriptions = vec![ - inscription("text/plain", [b'O'; 100]), - inscription("text/plain", [b'O'; 111]), - inscription("text/plain", [b'O'; 222]), - ]; - - let mode = Mode::SeparateOutputs; - - let fee_rate = 4.0.try_into().unwrap(); - - let (_commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint: None, - parent_info: None, - inscriptions, - destinations: reveal_addresses, - commit_fee_rate: fee_rate, - reveal_fee_rate: fee_rate, - no_limit: false, - reinscribe: false, - postage: Amount::from_sat(10_000), - mode, - ..Default::default() - } - .create_batch_inscription_transactions( - wallet_inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ) - .unwrap(); - - assert_eq!(reveal_tx.output.len(), 3); - assert!(reveal_tx - .output - .iter() - .all(|output| output.value == TARGET_POSTAGE.to_sat())); - } - - #[test] - fn batch_inscribe_into_separate_outputs_with_parent() { - let utxos = vec![ - (outpoint(1), Amount::from_sat(10_000)), - (outpoint(2), Amount::from_sat(50_000)), - ]; - - let parent = inscription_id(1); - - let parent_info = ParentInfo { - destination: change(3), - id: parent, - location: SatPoint { - outpoint: outpoint(1), - offset: 0, - }, - tx_out: TxOut { - script_pubkey: change(0).script_pubkey(), - value: 10000, - }, - }; - - let mut wallet_inscriptions = BTreeMap::new(); - wallet_inscriptions.insert(parent_info.location, parent); - - let commit_address = change(1); - let reveal_addresses = vec![recipient(), recipient(), recipient()]; - - let inscriptions = vec![ - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - InscriptionTemplate { - parent: Some(parent), - ..Default::default() - } - .into(), - ]; - - let mode = Mode::SeparateOutputs; - - let fee_rate = 4.0.try_into().unwrap(); - - let (commit_tx, reveal_tx, _private_key, _) = Batch { - satpoint: None, - parent_info: Some(parent_info.clone()), - inscriptions, - destinations: reveal_addresses, - commit_fee_rate: fee_rate, - reveal_fee_rate: fee_rate, - no_limit: false, - reinscribe: false, - postage: Amount::from_sat(10_000), - mode, - ..Default::default() - } - .create_batch_inscription_transactions( - wallet_inscriptions, - Chain::Signet, - BTreeSet::new(), - BTreeSet::new(), - utxos.into_iter().collect(), - [commit_address, change(2)], - ) - .unwrap(); - - assert_eq!( - parent, - ParsedEnvelope::from_transaction(&reveal_tx)[0] - .payload - .parent() - .unwrap() - ); - assert_eq!( - parent, - ParsedEnvelope::from_transaction(&reveal_tx)[1] - .payload - .parent() - .unwrap() - ); - - let sig_vbytes = 17; - let fee = fee_rate.fee(commit_tx.vsize() + sig_vbytes).to_sat(); - - let reveal_value = commit_tx - .output - .iter() - .map(|o| o.value) - .reduce(|acc, i| acc + i) - .unwrap(); - - assert_eq!(reveal_value, 50_000 - fee); - - assert_eq!( - reveal_tx.output[0].script_pubkey, - parent_info.destination.script_pubkey() - ); - assert_eq!(reveal_tx.output[0].value, parent_info.tx_out.value); - pretty_assert_eq!( - reveal_tx.input[0], - TxIn { - previous_output: parent_info.location.outpoint, - sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, - ..Default::default() - } - ); - } - - #[test] - fn example_batchfile_deserializes_successfully() { - Batchfile::load(Path::new("batch.yaml")).unwrap(); - } - - #[test] - fn flags_conflict_with_batch() { - for (flag, value) in [ - ("--file", Some("foo")), - ( - "--destination", - Some("tb1qsgx55dp6gn53tsmyjjv4c2ye403hgxynxs0dnm"), - ), - ("--cbor-metadata", Some("foo")), - ("--json-metadata", Some("foo")), - ( - "--satpoint", - Some("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0:0"), - ), - ("--reinscribe", None), - ("--metaprotocol", Some("foo")), - ( - "--parent", - Some("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33bi0"), - ), - ] { - let mut args = vec![ - "ord", - "wallet", - "inscribe", - "--fee-rate", - "1", - "--batch", - "foo.yaml", - flag, - ]; - - if let Some(value) = value { - args.push(value); - } - - assert!(Arguments::try_parse_from(args) - .unwrap_err() - .to_string() - .contains("the argument '--batch ' cannot be used with")); - } - } - - #[test] - fn batch_or_file_is_required() { - assert!( - Arguments::try_parse_from(["ord", "wallet", "inscribe", "--fee-rate", "1",]) - .unwrap_err() - .to_string() - .contains("error: the following required arguments were not provided:\n <--file |--batch >") - ); - } - - #[test] - fn satpoint_and_sat_flags_conflict() { - assert_regex_match!( - Arguments::try_parse_from([ - "ord", - "--index-sats", - "wallet", - "inscribe", - "--sat", - "50000000000", - "--satpoint", - "038112028c55f3f77cc0b8b413df51f70675f66be443212da0642b7636f68a00:1:0", - "--file", - "baz", - ]) - .unwrap_err() - .to_string(), - ".*--sat.*cannot be used with.*--satpoint.*" - ); - } +pub struct ParentInfo { + pub destination: Address, + pub id: InscriptionId, + pub location: SatPoint, + pub tx_out: TxOut, } diff --git a/src/wallet/inscribe/batch.rs b/src/wallet/inscribe/batch.rs index 78b1607e46..388b689f51 100644 --- a/src/wallet/inscribe/batch.rs +++ b/src/wallet/inscribe/batch.rs @@ -1,18 +1,18 @@ use super::*; -pub(super) struct Batch { - pub(super) commit_fee_rate: FeeRate, - pub(super) destinations: Vec
, - pub(super) dry_run: bool, - pub(super) inscriptions: Vec, - pub(super) mode: Mode, - pub(super) no_backup: bool, - pub(super) no_limit: bool, - pub(super) parent_info: Option, - pub(super) postage: Amount, - pub(super) reinscribe: bool, - pub(super) reveal_fee_rate: FeeRate, - pub(super) satpoint: Option, +pub(crate) struct Batch { + pub(crate) commit_fee_rate: FeeRate, + pub(crate) destinations: Vec
, + pub(crate) dry_run: bool, + pub(crate) inscriptions: Vec, + pub(crate) mode: Mode, + pub(crate) no_backup: bool, + pub(crate) no_limit: bool, + pub(crate) parent_info: Option, + pub(crate) postage: Amount, + pub(crate) reinscribe: bool, + pub(crate) reveal_fee_rate: FeeRate, + pub(crate) satpoint: Option, } impl Default for Batch { diff --git a/src/wallet/inscriptions.rs b/src/wallet/inscriptions.rs deleted file mode 100644 index 4113d27067..0000000000 --- a/src/wallet/inscriptions.rs +++ /dev/null @@ -1,37 +0,0 @@ -use super::*; - -#[derive(Serialize, Deserialize)] -pub struct Output { - pub inscription: InscriptionId, - pub location: SatPoint, - pub explorer: String, - pub postage: u64, -} - -pub(crate) fn run(wallet: Wallet) -> SubcommandResult { - let unspent_outputs = wallet.get_unspent_outputs()?; - - let inscriptions = wallet.get_inscriptions()?; - - let explorer = match wallet.chain() { - Chain::Mainnet => "https://ordinals.com/inscription/", - Chain::Regtest => "http://localhost/inscription/", - Chain::Signet => "https://signet.ordinals.com/inscription/", - Chain::Testnet => "https://testnet.ordinals.com/inscription/", - }; - - let mut output = Vec::new(); - - for (location, inscription) in inscriptions { - if let Some(postage) = unspent_outputs.get(&location.outpoint) { - output.push(Output { - location, - inscription, - explorer: format!("{explorer}{inscription}"), - postage: postage.to_sat(), - }) - } - } - - Ok(Some(Box::new(output))) -} diff --git a/src/wallet/outputs.rs b/src/wallet/outputs.rs deleted file mode 100644 index 121a6e7de6..0000000000 --- a/src/wallet/outputs.rs +++ /dev/null @@ -1,19 +0,0 @@ -use super::*; - -#[derive(Serialize, Deserialize)] -pub struct Output { - pub output: OutPoint, - pub amount: u64, -} - -pub(crate) fn run(wallet: Wallet) -> SubcommandResult { - let mut outputs = Vec::new(); - for (output, amount) in wallet.get_unspent_outputs()? { - outputs.push(Output { - output, - amount: amount.to_sat(), - }); - } - - Ok(Some(Box::new(outputs))) -} diff --git a/src/wallet/receive.rs b/src/wallet/receive.rs deleted file mode 100644 index 669540f6db..0000000000 --- a/src/wallet/receive.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::*; - -#[derive(Deserialize, Serialize)] -pub struct Output { - pub address: Address, -} - -pub(crate) fn run(wallet: Wallet) -> SubcommandResult { - let address = wallet - .bitcoin_client()? - .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; - - Ok(Some(Box::new(Output { address }))) -} diff --git a/src/wallet/restore.rs b/src/wallet/restore.rs deleted file mode 100644 index 9513594f9b..0000000000 --- a/src/wallet/restore.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::*; - -#[derive(Debug, Parser)] -pub(crate) struct Restore { - #[arg(help = "Restore wallet from ")] - mnemonic: Mnemonic, - #[arg( - long, - default_value = "", - help = "Use when deriving wallet" - )] - pub(crate) passphrase: String, -} - -impl Restore { - pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - wallet.initialize(self.mnemonic.to_seed(self.passphrase))?; - - Ok(None) - } -} diff --git a/src/wallet/sats.rs b/src/wallet/sats.rs deleted file mode 100644 index 35d3ea5584..0000000000 --- a/src/wallet/sats.rs +++ /dev/null @@ -1,321 +0,0 @@ -use super::*; - -#[derive(Debug, Parser)] -pub(crate) struct Sats { - #[arg( - long, - help = "Find satoshis listed in first column of tab-separated value file ." - )] - tsv: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct OutputTsv { - pub sat: String, - pub output: OutPoint, -} - -#[derive(Serialize, Deserialize)] -pub struct OutputRare { - pub sat: Sat, - pub output: OutPoint, - pub offset: u64, - pub rarity: Rarity, -} - -impl Sats { - pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { - ensure!( - wallet.check_sat_index()?, - "sats requires index created with `--index-sats` flag" - ); - - let utxos = wallet.get_output_sat_ranges()?; - - if let Some(path) = &self.tsv { - let mut output = Vec::new(); - for (outpoint, sat) in sats_from_tsv( - utxos, - &fs::read_to_string(path) - .with_context(|| format!("I/O error reading `{}`", path.display()))?, - )? { - output.push(OutputTsv { - sat: sat.into(), - output: outpoint, - }); - } - Ok(Some(Box::new(output))) - } else { - let mut output = Vec::new(); - for (outpoint, sat, offset, rarity) in rare_sats(utxos) { - output.push(OutputRare { - sat, - output: outpoint, - offset, - rarity, - }); - } - Ok(Some(Box::new(output))) - } - } -} - -fn rare_sats(utxos: Vec<(OutPoint, Vec<(u64, u64)>)>) -> Vec<(OutPoint, Sat, u64, Rarity)> { - utxos - .into_iter() - .flat_map(|(outpoint, sat_ranges)| { - let mut offset = 0; - sat_ranges.into_iter().filter_map(move |(start, end)| { - let sat = Sat(start); - let rarity = sat.rarity(); - let start_offset = offset; - offset += end - start; - if rarity > Rarity::Common { - Some((outpoint, sat, start_offset, rarity)) - } else { - None - } - }) - }) - .collect() -} - -fn sats_from_tsv( - utxos: Vec<(OutPoint, Vec<(u64, u64)>)>, - tsv: &str, -) -> Result> { - let mut needles = Vec::new(); - for (i, line) in tsv.lines().enumerate() { - if line.is_empty() || line.starts_with('#') { - continue; - } - - if let Some(value) = line.split('\t').next() { - let sat = Sat::from_str(value).map_err(|err| { - anyhow!( - "failed to parse sat from string \"{value}\" on line {}: {err}", - i + 1, - ) - })?; - - needles.push((sat, value)); - } - } - needles.sort(); - - let mut haystacks = utxos - .into_iter() - .flat_map(|(outpoint, ranges)| { - ranges - .into_iter() - .map(move |(start, end)| (start, end, outpoint)) - }) - .collect::>(); - haystacks.sort(); - - let mut i = 0; - let mut j = 0; - let mut results = Vec::new(); - while i < needles.len() && j < haystacks.len() { - let (needle, value) = needles[i]; - let (start, end, outpoint) = haystacks[j]; - - if needle >= start && needle < end { - results.push((outpoint, value)); - } - - if needle >= end { - j += 1; - } else { - i += 1; - } - } - - Ok(results) -} - -#[cfg(test)] -mod tests { - use {super::*, std::fmt::Write}; - - #[test] - fn identify_no_rare_sats() { - assert_eq!( - rare_sats(vec![( - outpoint(1), - vec![(51 * COIN_VALUE, 100 * COIN_VALUE), (1234, 5678)], - )]), - Vec::new() - ) - } - - #[test] - fn identify_one_rare_sat() { - assert_eq!( - rare_sats(vec![( - outpoint(1), - vec![(10, 80), (50 * COIN_VALUE, 100 * COIN_VALUE)], - )]), - vec![(outpoint(1), Sat(50 * COIN_VALUE), 70, Rarity::Uncommon)] - ) - } - - #[test] - fn identify_two_rare_sats() { - assert_eq!( - rare_sats(vec![( - outpoint(1), - vec![(0, 100), (1050000000000000, 1150000000000000)], - )]), - vec![ - (outpoint(1), Sat(0), 0, Rarity::Mythic), - (outpoint(1), Sat(1050000000000000), 100, Rarity::Epic) - ] - ) - } - - #[test] - fn identify_rare_sats_in_different_outpoints() { - assert_eq!( - rare_sats(vec![ - (outpoint(1), vec![(50 * COIN_VALUE, 55 * COIN_VALUE)]), - (outpoint(2), vec![(100 * COIN_VALUE, 111 * COIN_VALUE)],), - ]), - vec![ - (outpoint(1), Sat(50 * COIN_VALUE), 0, Rarity::Uncommon), - (outpoint(2), Sat(100 * COIN_VALUE), 0, Rarity::Uncommon) - ] - ) - } - - #[test] - fn identify_from_tsv_none() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "1\n").unwrap(), - Vec::new() - ) - } - - #[test] - fn identify_from_tsv_single() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n").unwrap(), - vec![(outpoint(1), "0"),] - ) - } - - #[test] - fn identify_from_tsv_two_in_one_range() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 2)])], "0\n1\n").unwrap(), - vec![(outpoint(1), "0"), (outpoint(1), "1"),] - ) - } - - #[test] - fn identify_from_tsv_out_of_order_tsv() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 2)])], "1\n0\n").unwrap(), - vec![(outpoint(1), "0"), (outpoint(1), "1"),] - ) - } - - #[test] - fn identify_from_tsv_out_of_order_ranges() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(1, 2), (0, 1)])], "1\n0\n").unwrap(), - vec![(outpoint(1), "0"), (outpoint(1), "1"),] - ) - } - - #[test] - fn identify_from_tsv_two_in_two_ranges() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1), (1, 2)])], "0\n1\n").unwrap(), - vec![(outpoint(1), "0"), (outpoint(1), "1"),] - ) - } - - #[test] - fn identify_from_tsv_two_in_two_outputs() { - assert_eq!( - sats_from_tsv( - vec![(outpoint(1), vec![(0, 1)]), (outpoint(2), vec![(1, 2)])], - "0\n1\n" - ) - .unwrap(), - vec![(outpoint(1), "0"), (outpoint(2), "1"),] - ) - } - - #[test] - fn identify_from_tsv_ignores_extra_columns() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\t===\n").unwrap(), - vec![(outpoint(1), "0"),] - ) - } - - #[test] - fn identify_from_tsv_ignores_empty_lines() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n\n\n").unwrap(), - vec![(outpoint(1), "0"),] - ) - } - - #[test] - fn identify_from_tsv_ignores_comments() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n#===\n").unwrap(), - vec![(outpoint(1), "0"),] - ) - } - - #[test] - fn parse_error_reports_line_and_value() { - assert_eq!( - sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n===\n") - .unwrap_err() - .to_string(), - "failed to parse sat from string \"===\" on line 2: invalid digit found in string", - ) - } - - #[test] - fn identify_from_tsv_is_fast() { - let mut start = 0; - let mut utxos = Vec::new(); - let mut results = Vec::new(); - for i in 0..16 { - let mut ranges = Vec::new(); - let outpoint = outpoint(i); - for _ in 0..100 { - let end = start + 50 * COIN_VALUE; - ranges.push((start, end)); - for j in 0..50 { - results.push((outpoint, start + j * COIN_VALUE)); - } - start = end; - } - utxos.push((outpoint, ranges)); - } - - let mut tsv = String::new(); - for i in 0..start / COIN_VALUE { - writeln!(tsv, "{}", i * COIN_VALUE).expect("writing to string should succeed"); - } - - let start = Instant::now(); - assert_eq!( - sats_from_tsv(utxos, &tsv) - .unwrap() - .into_iter() - .map(|(outpoint, s)| (outpoint, s.parse().unwrap())) - .collect::>(), - results - ); - - assert!(Instant::now() - start < Duration::from_secs(10)); - } -} diff --git a/src/wallet/send.rs b/src/wallet/send.rs deleted file mode 100644 index f080636ac0..0000000000 --- a/src/wallet/send.rs +++ /dev/null @@ -1,261 +0,0 @@ -use {super::*, wallet::transaction_builder::Target}; - -#[derive(Debug, Parser)] -pub(crate) struct Send { - address: Address, - outgoing: Outgoing, - #[arg(long, help = "Use fee rate of sats/vB")] - fee_rate: FeeRate, - #[arg( - long, - help = "Target amount of postage to include with sent inscriptions. Default `10000sat`" - )] - pub(crate) postage: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct Output { - pub transaction: Txid, -} - -impl Send { - pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - let address = self - .address - .clone() - .require_network(wallet.chain().network())?; - - let unspent_outputs = wallet.get_unspent_outputs()?; - - let locked_outputs = wallet.get_locked_outputs()?; - - let inscriptions = wallet.get_inscriptions()?; - - let runic_outputs = wallet.get_runic_outputs()?; - - let satpoint = match self.outgoing { - Outgoing::Amount(amount) => { - Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; - let transaction = Self::send_amount(&wallet, amount, address, self.fee_rate)?; - return Ok(Some(Box::new(Output { transaction }))); - } - Outgoing::InscriptionId(id) => wallet.get_inscription_satpoint(id)?, - Outgoing::Rune { decimal, rune } => { - let transaction = Self::send_runes( - address, - decimal, - self.fee_rate, - inscriptions, - rune, - runic_outputs, - unspent_outputs, - &wallet, - )?; - return Ok(Some(Box::new(Output { transaction }))); - } - Outgoing::SatPoint(satpoint) => { - for inscription_satpoint in inscriptions.keys() { - if satpoint == *inscription_satpoint { - bail!("inscriptions must be sent by inscription ID"); - } - } - - ensure!( - !runic_outputs.contains(&satpoint.outpoint), - "runic outpoints may not be sent by satpoint" - ); - - satpoint - } - }; - - let change = [wallet.get_change_address()?, wallet.get_change_address()?]; - - let postage = if let Some(postage) = self.postage { - Target::ExactPostage(postage) - } else { - Target::Postage - }; - - let unsigned_transaction = TransactionBuilder::new( - satpoint, - inscriptions, - unspent_outputs, - locked_outputs, - runic_outputs, - address.clone(), - change, - self.fee_rate, - postage, - ) - .build_transaction()?; - - let signed_tx = wallet - .bitcoin_client()? - .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? - .hex; - - let txid = wallet.bitcoin_client()?.send_raw_transaction(&signed_tx)?; - - Ok(Some(Box::new(Output { transaction: txid }))) - } - - fn lock_non_cardinal_outputs( - wallet: &Wallet, - inscriptions: &BTreeMap, - runic_outputs: &BTreeSet, - unspent_outputs: BTreeMap, - ) -> Result { - let all_inscription_outputs = inscriptions - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - let locked_outputs = unspent_outputs - .keys() - .filter(|utxo| all_inscription_outputs.contains(utxo)) - .chain(runic_outputs.iter()) - .cloned() - .collect::>(); - - if !wallet.bitcoin_client()?.lock_unspent(&locked_outputs)? { - bail!("failed to lock UTXOs"); - } - - Ok(()) - } - - fn send_amount( - wallet: &Wallet, - amount: Amount, - address: Address, - fee_rate: FeeRate, - ) -> Result { - Ok(wallet.bitcoin_client()?.call( - "sendtoaddress", - &[ - address.to_string().into(), // 1. address - amount.to_btc().into(), // 2. amount - serde_json::Value::Null, // 3. comment - serde_json::Value::Null, // 4. comment_to - serde_json::Value::Null, // 5. subtractfeefromamount - serde_json::Value::Null, // 6. replaceable - serde_json::Value::Null, // 7. conf_target - serde_json::Value::Null, // 8. estimate_mode - serde_json::Value::Null, // 9. avoid_reuse - fee_rate.n().into(), // 10. fee_rate - ], - )?) - } - - fn send_runes( - address: Address, - decimal: Decimal, - fee_rate: FeeRate, - inscriptions: BTreeMap, - spaced_rune: SpacedRune, - runic_outputs: BTreeSet, - unspent_outputs: BTreeMap, - wallet: &Wallet, - ) -> Result { - ensure!( - wallet.check_rune_index()?, - "sending runes with `ord send` requires index created with `--index-runes` flag", - ); - - Self::lock_non_cardinal_outputs(wallet, &inscriptions, &runic_outputs, unspent_outputs)?; - - let (id, entry, _parent) = wallet - .get_rune(spaced_rune.rune)? - .with_context(|| format!("rune `{}` has not been etched", spaced_rune.rune))?; - - let amount = decimal.to_amount(entry.divisibility)?; - - let inscribed_outputs = inscriptions - .keys() - .map(|satpoint| satpoint.outpoint) - .collect::>(); - - let mut input_runes = 0; - let mut input = Vec::new(); - - for output in runic_outputs { - if inscribed_outputs.contains(&output) { - continue; - } - - let balance = wallet.get_rune_balance_in_output(&output, entry.rune)?; - - if balance > 0 { - input_runes += balance; - input.push(output); - } - - if input_runes >= amount { - break; - } - } - - ensure! { - input_runes >= amount, - "insufficient `{}` balance, only {} in wallet", - spaced_rune, - Pile { - amount: input_runes, - divisibility: entry.divisibility, - symbol: entry.symbol - }, - } - - let runestone = Runestone { - edicts: vec![Edict { - amount, - id: id.into(), - output: 2, - }], - ..Default::default() - }; - - let unfunded_transaction = Transaction { - version: 2, - lock_time: LockTime::ZERO, - input: input - .into_iter() - .map(|previous_output| TxIn { - previous_output, - script_sig: ScriptBuf::new(), - sequence: Sequence::MAX, - witness: Witness::new(), - }) - .collect(), - output: vec![ - TxOut { - script_pubkey: runestone.encipher(), - value: 0, - }, - TxOut { - script_pubkey: wallet.get_change_address()?.script_pubkey(), - value: TARGET_POSTAGE.to_sat(), - }, - TxOut { - script_pubkey: address.script_pubkey(), - value: TARGET_POSTAGE.to_sat(), - }, - ], - }; - - let unsigned_transaction = - fund_raw_transaction(&wallet.bitcoin_client()?, fee_rate, &unfunded_transaction)?; - - let signed_transaction = wallet - .bitcoin_client()? - .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? - .hex; - - Ok( - wallet - .bitcoin_client()? - .send_raw_transaction(&signed_transaction)?, - ) - } -} diff --git a/src/wallet/transactions.rs b/src/wallet/transactions.rs deleted file mode 100644 index f0ad7d9057..0000000000 --- a/src/wallet/transactions.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::*; - -#[derive(Debug, Parser)] -pub(crate) struct Transactions { - #[arg(long, help = "Fetch at most transactions.")] - limit: Option, -} - -#[derive(Serialize, Deserialize)] -pub struct Output { - pub transaction: Txid, - pub confirmations: i32, -} - -impl Transactions { - pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { - let client = wallet.bitcoin_client()?; - - let mut output = Vec::new(); - for tx in client.list_transactions( - None, - Some(self.limit.unwrap_or(u16::MAX).into()), - None, - None, - )? { - output.push(Output { - transaction: tx.info.txid, - confirmations: tx.info.confirmations, - }); - } - - Ok(Some(Box::new(output))) - } -} From a22ca3db0e873dd7bf0b269e3b52884c693fcd2a Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 15 Jan 2024 21:37:54 +0100 Subject: [PATCH 41/74] Horribel state 2 --- src/subcommand/wallet.rs | 132 +++ src/subcommand/wallet/balance.rs | 70 ++ src/subcommand/wallet/cardinals.rs | 33 + src/subcommand/wallet/create.rs | 36 + src/subcommand/wallet/etch.rs | 131 +++ src/subcommand/wallet/inscribe.rs | 1312 +++++++++++++++++++++++++ src/subcommand/wallet/inscriptions.rs | 37 + src/subcommand/wallet/outputs.rs | 19 + src/subcommand/wallet/receive.rs | 14 + src/subcommand/wallet/restore.rs | 21 + src/subcommand/wallet/sats.rs | 321 ++++++ src/subcommand/wallet/send.rs | 261 +++++ src/subcommand/wallet/transactions.rs | 34 + 13 files changed, 2421 insertions(+) create mode 100644 src/subcommand/wallet.rs create mode 100644 src/subcommand/wallet/balance.rs create mode 100644 src/subcommand/wallet/cardinals.rs create mode 100644 src/subcommand/wallet/create.rs create mode 100644 src/subcommand/wallet/etch.rs create mode 100644 src/subcommand/wallet/inscribe.rs create mode 100644 src/subcommand/wallet/inscriptions.rs create mode 100644 src/subcommand/wallet/outputs.rs create mode 100644 src/subcommand/wallet/receive.rs create mode 100644 src/subcommand/wallet/restore.rs create mode 100644 src/subcommand/wallet/sats.rs create mode 100644 src/subcommand/wallet/send.rs create mode 100644 src/subcommand/wallet/transactions.rs diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs new file mode 100644 index 0000000000..affbb7a22c --- /dev/null +++ b/src/subcommand/wallet.rs @@ -0,0 +1,132 @@ +use { + super::*, + crate::wallet::{ + inscribe::{ + batch::{Batch, Batchfile, Mode}, + }, + Wallet, + }, + reqwest::Url, +}; + +pub mod balance; +pub mod cardinals; +pub mod create; +pub mod etch; +pub mod inscribe; +pub mod inscriptions; +pub mod outputs; +pub mod receive; +pub mod restore; +pub mod sats; +pub mod send; +pub mod transactions; + +#[derive(Debug, Parser)] +pub(crate) struct WalletCommand { + #[arg(long, default_value = "ord", help = "Use wallet named .")] + pub(crate) name: String, + #[arg(long, alias = "nosync", help = "Do not update index.")] + pub(crate) no_sync: bool, + #[command(subcommand)] + pub(crate) subcommand: Subcommand, +} + +#[derive(Debug, Parser)] +pub(crate) enum Subcommand { + #[command(about = "Get wallet balance")] + Balance, + #[command(about = "Create new wallet")] + Create(create::Create), + #[command(about = "Create rune")] + Etch(etch::Etch), + #[command(about = "Create inscription")] + Inscribe(inscribe::Inscribe), + #[command(about = "List wallet inscriptions")] + Inscriptions, + #[command(about = "Generate receive address")] + Receive, + #[command(about = "Restore wallet")] + Restore(restore::Restore), + #[command(about = "List wallet satoshis")] + Sats(sats::Sats), + #[command(about = "Send sat or inscription")] + Send(send::Send), + #[command(about = "See wallet transactions")] + Transactions(transactions::Transactions), + #[command(about = "List all unspent outputs in wallet")] + Outputs, + #[command(about = "List unspent cardinal outputs in wallet")] + Cardinals, +} + +impl WalletCommand { + pub(crate) fn run(self, options: Options) -> SubcommandResult { + let index = Arc::new(Index::open(&options)?); + let handle = axum_server::Handle::new(); + LISTENERS.lock().unwrap().push(handle.clone()); + + let ord_url: Url = { + format!( + "http://127.0.0.1:{}", + TcpListener::bind("127.0.0.1:0")?.local_addr()?.port() // very hacky + ) + .parse() + .unwrap() + }; + + { + let options = options.clone(); + let ord_url = ord_url.clone(); + std::thread::spawn(move || { + crate::subcommand::server::Server { + address: ord_url.host_str().map(|a| a.to_string()), + acme_domain: vec![], + csp_origin: None, + http_port: ord_url.port(), + https_port: None, + acme_cache: None, + acme_contact: vec![], + http: true, + https: false, + redirect_http_to_https: false, + enable_json_api: true, + decompress: false, + no_sync: self.no_sync, + } + .run(options, index, handle) + .unwrap() + }); + } + + let wallet = Wallet { + no_sync: self.no_sync, + options, + ord_url, + name: self.name.clone(), + }; + + let result = match self.subcommand { + Subcommand::Balance => balance::run(wallet), + Subcommand::Create(create) => create.run(wallet), + Subcommand::Etch(etch) => etch.run(wallet), + Subcommand::Inscribe(inscribe) => inscribe.run(wallet), + Subcommand::Inscriptions => inscriptions::run(wallet), + Subcommand::Receive => receive::run(wallet), + Subcommand::Restore(restore) => restore.run(wallet), + Subcommand::Sats(sats) => sats.run(wallet), + Subcommand::Send(send) => send.run(wallet), + Subcommand::Transactions(transactions) => transactions.run(wallet), + Subcommand::Outputs => outputs::run(wallet), + Subcommand::Cardinals => cardinals::run(wallet), + }; + + LISTENERS + .lock() + .unwrap() + .iter() + .for_each(|handle| handle.shutdown()); + + result + } +} diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs new file mode 100644 index 0000000000..5d68d8a0b3 --- /dev/null +++ b/src/subcommand/wallet/balance.rs @@ -0,0 +1,70 @@ +use {super::*, std::collections::BTreeSet}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct Output { + pub cardinal: u64, + pub ordinal: u64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub runes: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub runic: Option, + pub total: u64, +} + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let unspent_outputs = wallet.get_unspent_outputs()?; + + let inscription_outputs = wallet + .get_inscriptions()? + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + let mut cardinal = 0; + let mut ordinal = 0; + let mut runes = BTreeMap::new(); + let mut runic = 0; + + for (output, amount) in unspent_outputs { + let rune_balances = wallet.get_runes_balances_for_output(&output)?; + + if inscription_outputs.contains(&output) { + ordinal += amount.to_sat(); + } else if !rune_balances.is_empty() { + for (spaced_rune, pile) in rune_balances { + *runes.entry(spaced_rune.rune).or_default() += pile.amount; + } + runic += amount.to_sat(); + } else { + cardinal += amount.to_sat(); + } + } + + Ok(Some(Box::new(Output { + cardinal, + ordinal, + runes: wallet.check_rune_index()?.then_some(runes), + runic: wallet.check_rune_index()?.then_some(runic), + total: cardinal + ordinal + runic, + }))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn runes_and_runic_fields_are_not_present_if_none() { + assert_eq!( + serde_json::to_string(&Output { + cardinal: 0, + ordinal: 0, + runes: None, + runic: None, + total: 0 + }) + .unwrap(), + r#"{"cardinal":0,"ordinal":0,"total":0}"# + ); + } +} diff --git a/src/subcommand/wallet/cardinals.rs b/src/subcommand/wallet/cardinals.rs new file mode 100644 index 0000000000..a7389a4b54 --- /dev/null +++ b/src/subcommand/wallet/cardinals.rs @@ -0,0 +1,33 @@ +use {super::*, std::collections::BTreeSet}; + +#[derive(Serialize, Deserialize)] +pub struct CardinalUtxo { + pub output: OutPoint, + pub amount: u64, +} + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let unspent_outputs = wallet.get_unspent_outputs()?; + + let inscribed_utxos = wallet + .get_inscriptions()? + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + let cardinal_utxos = unspent_outputs + .iter() + .filter_map(|(output, amount)| { + if inscribed_utxos.contains(output) { + None + } else { + Some(CardinalUtxo { + output: *output, + amount: amount.to_sat(), + }) + } + }) + .collect::>(); + + Ok(Some(Box::new(cardinal_utxos))) +} diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs new file mode 100644 index 0000000000..3e77becce8 --- /dev/null +++ b/src/subcommand/wallet/create.rs @@ -0,0 +1,36 @@ +use { + super::*, + bitcoin::secp256k1::rand::{self, RngCore}, +}; + +#[derive(Serialize, Deserialize)] +pub struct Output { + pub mnemonic: Mnemonic, + pub passphrase: Option, +} + +#[derive(Debug, Parser)] +pub(crate) struct Create { + #[arg( + long, + default_value = "", + help = "Use to derive wallet seed." + )] + pub(crate) passphrase: String, +} + +impl Create { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + let mut entropy = [0; 16]; + rand::thread_rng().fill_bytes(&mut entropy); + + let mnemonic = Mnemonic::from_entropy(&entropy)?; + + wallet.initialize(mnemonic.to_seed(self.passphrase.clone()))?; + + Ok(Some(Box::new(Output { + mnemonic, + passphrase: Some(self.passphrase), + }))) + } +} diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs new file mode 100644 index 0000000000..e290426ca6 --- /dev/null +++ b/src/subcommand/wallet/etch.rs @@ -0,0 +1,131 @@ +use super::*; + +#[derive(Debug, Parser)] +pub(crate) struct Etch { + #[clap(long, help = "Set divisibility to .")] + divisibility: u8, + #[clap(long, help = "Etch with fee rate of sats/vB.")] + fee_rate: FeeRate, + #[clap(long, help = "Etch rune . May contain `.` or `โ€ข`as spacers.")] + rune: SpacedRune, + #[clap(long, help = "Set supply to .")] + supply: Decimal, + #[clap(long, help = "Set currency symbol to .")] + symbol: char, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Output { + pub rune: SpacedRune, + pub transaction: Txid, +} + +impl Etch { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + ensure!( + wallet.check_rune_index()?, + "`ord wallet etch` requires index created with `--index-runes` flag", + ); + + let SpacedRune { rune, spacers } = self.rune; + + let count = wallet.bitcoin_client()?.get_block_count()?; + + ensure!( + wallet.get_rune(rune)?.is_none(), + "rune `{}` has already been etched", + rune, + ); + + let minimum_at_height = + Rune::minimum_at_height(wallet.chain(), Height(u32::try_from(count).unwrap() + 1)); + + ensure!( + rune >= minimum_at_height, + "rune is less than minimum for next block: {} < {minimum_at_height}", + rune, + ); + + ensure!(!rune.is_reserved(), "rune `{}` is reserved", rune); + + ensure!( + self.divisibility <= crate::runes::MAX_DIVISIBILITY, + " must be equal to or less than 38" + ); + + let destination = wallet.get_change_address()?; + + let runestone = Runestone { + etching: Some(Etching { + deadline: None, + divisibility: self.divisibility, + limit: None, + rune: Some(rune), + spacers, + symbol: Some(self.symbol), + term: None, + }), + edicts: vec![Edict { + amount: self.supply.to_amount(self.divisibility)?, + id: 0, + output: 1, + }], + default_output: None, + burn: false, + }; + + let script_pubkey = runestone.encipher(); + + ensure!( + script_pubkey.len() <= 82, + "runestone greater than maximum OP_RETURN size: {} > 82", + script_pubkey.len() + ); + + let unfunded_transaction = Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: Vec::new(), + output: vec![ + TxOut { + script_pubkey, + value: 0, + }, + TxOut { + script_pubkey: destination.script_pubkey(), + value: TARGET_POSTAGE.to_sat(), + }, + ], + }; + + let inscriptions = wallet + .get_inscriptions()? + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + if !wallet.bitcoin_client()?.lock_unspent(&inscriptions)? { + bail!("failed to lock UTXOs"); + } + + let unsigned_transaction = fund_raw_transaction( + &wallet.bitcoin_client()?, + self.fee_rate, + &unfunded_transaction, + )?; + + let signed_transaction = wallet + .bitcoin_client()? + .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? + .hex; + + let transaction = wallet + .bitcoin_client()? + .send_raw_transaction(&signed_transaction)?; + + Ok(Some(Box::new(Output { + rune: self.rune, + transaction, + }))) + } +} diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs new file mode 100644 index 0000000000..896b8d627a --- /dev/null +++ b/src/subcommand/wallet/inscribe.rs @@ -0,0 +1,1312 @@ +use super::*; + +#[derive(Debug, Parser)] +#[clap( + group = ArgGroup::new("source") + .required(true) + .args(&["file", "batch"]), +)] +pub(crate) struct Inscribe { + #[arg( + long, + help = "Inscribe multiple inscriptions defined in a yaml .", + conflicts_with_all = &[ + "cbor_metadata", "destination", "file", "json_metadata", "metaprotocol", "parent", "postage", "reinscribe", "satpoint" + ] + )] + pub(crate) batch: Option, + #[arg( + long, + help = "Include CBOR in file at as inscription metadata", + conflicts_with = "json_metadata" + )] + pub(crate) cbor_metadata: Option, + #[arg( + long, + help = "Use sats/vbyte for commit transaction.\nDefaults to if unset." + )] + pub(crate) commit_fee_rate: Option, + #[arg(long, help = "Compress inscription content with brotli.")] + pub(crate) compress: bool, + #[arg(long, help = "Send inscription to .")] + pub(crate) destination: Option>, + #[arg(long, help = "Don't sign or broadcast transactions.")] + pub(crate) dry_run: bool, + #[arg(long, help = "Use fee rate of sats/vB.")] + pub(crate) fee_rate: FeeRate, + #[arg(long, help = "Inscribe sat with contents of .")] + pub(crate) file: Option, + #[arg( + long, + help = "Include JSON in file at converted to CBOR as inscription metadata", + conflicts_with = "cbor_metadata" + )] + pub(crate) json_metadata: Option, + #[clap(long, help = "Set inscription metaprotocol to .")] + pub(crate) metaprotocol: Option, + #[arg(long, alias = "nobackup", help = "Do not back up recovery key.")] + pub(crate) no_backup: bool, + #[arg( + long, + alias = "nolimit", + help = "Do not check that transactions are equal to or below the MAX_STANDARD_TX_WEIGHT of 400,000 weight units. Transactions over this limit are currently nonstandard and will not be relayed by bitcoind in its default configuration. Do not use this flag unless you understand the implications." + )] + pub(crate) no_limit: bool, + #[clap(long, help = "Make inscription a child of .")] + pub(crate) parent: Option, + #[arg( + long, + help = "Amount of postage to include in the inscription. Default `10000sat`." + )] + pub(crate) postage: Option, + #[clap(long, help = "Allow reinscription.")] + pub(crate) reinscribe: bool, + #[arg(long, help = "Inscribe .")] + pub(crate) satpoint: Option, + #[arg(long, help = "Inscribe .", conflicts_with = "satpoint")] + pub(crate) sat: Option, +} + +impl Inscribe { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + let metadata = Inscribe::parse_metadata(self.cbor_metadata, self.json_metadata)?; + + let utxos = wallet.get_unspent_outputs()?; + + let locked_utxos = wallet.get_locked_outputs()?; + + let runic_utxos = wallet.get_runic_outputs()?; + + let chain = wallet.chain(); + + let postage; + let destinations; + let inscriptions; + let mode; + let parent_info; + let sat; + + match (self.file, self.batch) { + (Some(file), None) => { + parent_info = wallet.get_parent_info(self.parent, &utxos)?; + + postage = self.postage.unwrap_or(TARGET_POSTAGE); + + inscriptions = vec![Inscription::from_file( + chain, + file, + self.parent, + None, + self.metaprotocol, + metadata, + self.compress, + )?]; + + mode = Mode::SeparateOutputs; + + sat = self.sat; + + destinations = vec![match self.destination.clone() { + Some(destination) => destination.require_network(chain.network())?, + None => wallet.get_change_address()?, + }]; + } + (None, Some(batch)) => { + let batchfile = Batchfile::load(&batch)?; + + parent_info = wallet.get_parent_info(batchfile.parent, &utxos)?; + + postage = batchfile + .postage + .map(Amount::from_sat) + .unwrap_or(TARGET_POSTAGE); + + (inscriptions, destinations) = batchfile.inscriptions( + &wallet, + parent_info.as_ref().map(|info| info.tx_out.value), + metadata, + postage, + self.compress, + )?; + + mode = batchfile.mode; + + if batchfile.sat.is_some() && mode != Mode::SameSat { + return Err(anyhow!("`sat` can only be set in `same-sat` mode")); + } + + sat = batchfile.sat; + } + _ => unreachable!(), + } + + let satpoint = if let Some(sat) = sat { + Some(wallet.find_sat_in_outputs(sat, &utxos)?) + } else { + self.satpoint + }; + + Batch { + commit_fee_rate: self.commit_fee_rate.unwrap_or(self.fee_rate), + destinations, + dry_run: self.dry_run, + inscriptions, + mode, + no_backup: self.no_backup, + no_limit: self.no_limit, + parent_info, + postage, + reinscribe: self.reinscribe, + reveal_fee_rate: self.fee_rate, + satpoint, + } + .inscribe(&locked_utxos, runic_utxos, &utxos, &wallet) + } + + fn parse_metadata(cbor: Option, json: Option) -> Result>> { + if let Some(path) = cbor { + let cbor = fs::read(path)?; + let _value: Value = ciborium::from_reader(Cursor::new(cbor.clone())) + .context("failed to parse CBOR metadata")?; + + Ok(Some(cbor)) + } else if let Some(path) = json { + let value: serde_json::Value = + serde_json::from_reader(File::open(path)?).context("failed to parse JSON metadata")?; + let mut cbor = Vec::new(); + ciborium::into_writer(&value, &mut cbor)?; + + Ok(Some(cbor)) + } else { + Ok(None) + } + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::wallet::inscribe::batch::BatchEntry, + bitcoin::policy::MAX_STANDARD_TX_WEIGHT, + serde_yaml::{Mapping, Value}, + }; + + #[test] + fn reveal_transaction_pays_fee() { + let utxos = vec![(outpoint(1), Amount::from_sat(20000))]; + let inscription = inscription("text/plain", "ord"); + let commit_address = change(0); + let reveal_address = recipient(); + let change = [commit_address, change(1)]; + + let (commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint: Some(satpoint(1, 0)), + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(1.0).unwrap(), + reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + BTreeMap::new(), + Chain::Mainnet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + change, + ) + .unwrap(); + + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + let fee = Amount::from_sat((1.0 * (reveal_tx.vsize() as f64)).ceil() as u64); + + assert_eq!( + reveal_tx.output[0].value, + 20000 - fee.to_sat() - (20000 - commit_tx.output[0].value), + ); + } + + #[test] + fn inscribe_transactions_opt_in_to_rbf() { + let utxos = vec![(outpoint(1), Amount::from_sat(20000))]; + let inscription = inscription("text/plain", "ord"); + let commit_address = change(0); + let reveal_address = recipient(); + let change = [commit_address, change(1)]; + + let (commit_tx, reveal_tx, _, _) = Batch { + satpoint: Some(satpoint(1, 0)), + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(1.0).unwrap(), + reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + BTreeMap::new(), + Chain::Mainnet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + change, + ) + .unwrap(); + + assert!(commit_tx.is_explicitly_rbf()); + assert!(reveal_tx.is_explicitly_rbf()); + } + + #[test] + fn inscribe_with_no_satpoint_and_no_cardinal_utxos() { + let utxos = vec![(outpoint(1), Amount::from_sat(1000))]; + let mut inscriptions = BTreeMap::new(); + inscriptions.insert( + SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + inscription_id(1), + ); + + let inscription = inscription("text/plain", "ord"); + let satpoint = None; + let commit_address = change(0); + let reveal_address = recipient(); + + let error = Batch { + satpoint, + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(1.0).unwrap(), + reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + inscriptions, + Chain::Mainnet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(1)], + ) + .unwrap_err() + .to_string(); + + assert!( + error.contains("wallet contains no cardinal utxos"), + "{}", + error + ); + } + + #[test] + fn inscribe_with_no_satpoint_and_enough_cardinal_utxos() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(20_000)), + (outpoint(2), Amount::from_sat(20_000)), + ]; + let mut inscriptions = BTreeMap::new(); + inscriptions.insert( + SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + inscription_id(1), + ); + + let inscription = inscription("text/plain", "ord"); + let satpoint = None; + let commit_address = change(0); + let reveal_address = recipient(); + + assert!(Batch { + satpoint, + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(1.0).unwrap(), + reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + inscriptions, + Chain::Mainnet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(1)], + ) + .is_ok()) + } + + #[test] + fn inscribe_with_custom_fee_rate() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(20_000)), + ]; + let mut inscriptions = BTreeMap::new(); + inscriptions.insert( + SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + inscription_id(1), + ); + + let inscription = inscription("text/plain", "ord"); + let satpoint = None; + let commit_address = change(0); + let reveal_address = recipient(); + let fee_rate = 3.3; + + let (commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint, + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(fee_rate).unwrap(), + reveal_fee_rate: FeeRate::try_from(fee_rate).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(1)], + ) + .unwrap(); + + let sig_vbytes = 17; + let fee = FeeRate::try_from(fee_rate) + .unwrap() + .fee(commit_tx.vsize() + sig_vbytes) + .to_sat(); + + let reveal_value = commit_tx + .output + .iter() + .map(|o| o.value) + .reduce(|acc, i| acc + i) + .unwrap(); + + assert_eq!(reveal_value, 20_000 - fee); + + let fee = FeeRate::try_from(fee_rate) + .unwrap() + .fee(reveal_tx.vsize()) + .to_sat(); + + assert_eq!( + reveal_tx.output[0].value, + 20_000 - fee - (20_000 - commit_tx.output[0].value), + ); + } + + #[test] + fn inscribe_with_parent() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(20_000)), + ]; + + let mut inscriptions = BTreeMap::new(); + let parent_inscription = inscription_id(1); + let parent_info = ParentInfo { + destination: change(3), + id: parent_inscription, + location: SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + tx_out: TxOut { + script_pubkey: change(0).script_pubkey(), + value: 10000, + }, + }; + + inscriptions.insert(parent_info.location, parent_inscription); + + let child_inscription = InscriptionTemplate { + parent: Some(parent_inscription), + ..Default::default() + } + .into(); + + let commit_address = change(1); + let reveal_address = recipient(); + let fee_rate = 4.0; + + let (commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint: None, + parent_info: Some(parent_info.clone()), + inscriptions: vec![child_inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(fee_rate).unwrap(), + reveal_fee_rate: FeeRate::try_from(fee_rate).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ) + .unwrap(); + + let sig_vbytes = 17; + let fee = FeeRate::try_from(fee_rate) + .unwrap() + .fee(commit_tx.vsize() + sig_vbytes) + .to_sat(); + + let reveal_value = commit_tx + .output + .iter() + .map(|o| o.value) + .reduce(|acc, i| acc + i) + .unwrap(); + + assert_eq!(reveal_value, 20_000 - fee); + + let sig_vbytes = 16; + let fee = FeeRate::try_from(fee_rate) + .unwrap() + .fee(reveal_tx.vsize() + sig_vbytes) + .to_sat(); + + assert_eq!(fee, commit_tx.output[0].value - reveal_tx.output[1].value,); + assert_eq!( + reveal_tx.output[0].script_pubkey, + parent_info.destination.script_pubkey() + ); + assert_eq!(reveal_tx.output[0].value, parent_info.tx_out.value); + pretty_assert_eq!( + reveal_tx.input[0], + TxIn { + previous_output: parent_info.location.outpoint, + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + ..Default::default() + } + ); + } + + #[test] + fn inscribe_with_commit_fee_rate() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(20_000)), + ]; + let mut inscriptions = BTreeMap::new(); + inscriptions.insert( + SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + inscription_id(1), + ); + + let inscription = inscription("text/plain", "ord"); + let satpoint = None; + let commit_address = change(0); + let reveal_address = recipient(); + let commit_fee_rate = 3.3; + let fee_rate = 1.0; + + let (commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint, + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(commit_fee_rate).unwrap(), + reveal_fee_rate: FeeRate::try_from(fee_rate).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(1)], + ) + .unwrap(); + + let sig_vbytes = 17; + let fee = FeeRate::try_from(commit_fee_rate) + .unwrap() + .fee(commit_tx.vsize() + sig_vbytes) + .to_sat(); + + let reveal_value = commit_tx + .output + .iter() + .map(|o| o.value) + .reduce(|acc, i| acc + i) + .unwrap(); + + assert_eq!(reveal_value, 20_000 - fee); + + let fee = FeeRate::try_from(fee_rate) + .unwrap() + .fee(reveal_tx.vsize()) + .to_sat(); + + assert_eq!( + reveal_tx.output[0].value, + 20_000 - fee - (20_000 - commit_tx.output[0].value), + ); + } + + #[test] + fn inscribe_over_max_standard_tx_weight() { + let utxos = vec![(outpoint(1), Amount::from_sat(50 * COIN_VALUE))]; + + let inscription = inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize]); + let satpoint = None; + let commit_address = change(0); + let reveal_address = recipient(); + + let error = Batch { + satpoint, + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(1.0).unwrap(), + reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), + no_limit: false, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + BTreeMap::new(), + Chain::Mainnet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(1)], + ) + .unwrap_err() + .to_string(); + + assert!( + error.contains(&format!("reveal transaction weight greater than {MAX_STANDARD_TX_WEIGHT} (MAX_STANDARD_TX_WEIGHT): 402799")), + "{}", + error + ); + } + + #[test] + fn inscribe_with_no_max_standard_tx_weight() { + let utxos = vec![(outpoint(1), Amount::from_sat(50 * COIN_VALUE))]; + + let inscription = inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize]); + let satpoint = None; + let commit_address = change(0); + let reveal_address = recipient(); + + let (_commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint, + parent_info: None, + inscriptions: vec![inscription], + destinations: vec![reveal_address], + commit_fee_rate: FeeRate::try_from(1.0).unwrap(), + reveal_fee_rate: FeeRate::try_from(1.0).unwrap(), + no_limit: true, + reinscribe: false, + postage: TARGET_POSTAGE, + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + BTreeMap::new(), + Chain::Mainnet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(1)], + ) + .unwrap(); + + assert!(reveal_tx.size() >= MAX_STANDARD_TX_WEIGHT as usize); + } + + #[test] + fn cbor_and_json_metadata_flags_conflict() { + assert_regex_match!( + Arguments::try_parse_from([ + "ord", + "wallet", + "inscribe", + "--cbor-metadata", + "foo", + "--json-metadata", + "bar", + "--file", + "baz", + ]) + .unwrap_err() + .to_string(), + ".*--cbor-metadata.*cannot be used with.*--json-metadata.*" + ); + } + + #[test] + fn batch_is_loaded_from_yaml_file() { + let parent = "8d363b28528b0cb86b5fd48615493fb175bdf132d2a3d20b4251bba3f130a5abi0" + .parse::() + .unwrap(); + + let tempdir = TempDir::new().unwrap(); + + let inscription_path = tempdir.path().join("tulip.txt"); + fs::write(&inscription_path, "tulips are pretty").unwrap(); + + let brc20_path = tempdir.path().join("token.json"); + + let batch_path = tempdir.path().join("batch.yaml"); + fs::write( + &batch_path, + format!( + "mode: separate-outputs +parent: {parent} +inscriptions: +- file: {} + metadata: + title: Lorem Ipsum + description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tristique, massa nec condimentum venenatis, ante massa tempor velit, et accumsan ipsum ligula a massa. Nunc quis orci ante. +- file: {} + metaprotocol: brc-20 +", + inscription_path.display(), + brc20_path.display() + ), + ) + .unwrap(); + + let mut metadata = Mapping::new(); + metadata.insert( + Value::String("title".to_string()), + Value::String("Lorem Ipsum".to_string()), + ); + metadata.insert(Value::String("description".to_string()), Value::String("Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tristique, massa nec condimentum venenatis, ante massa tempor velit, et accumsan ipsum ligula a massa. Nunc quis orci ante.".to_string())); + + assert_eq!( + Batchfile::load(&batch_path).unwrap(), + Batchfile { + inscriptions: vec![ + BatchEntry { + file: inscription_path, + metadata: Some(Value::Mapping(metadata)), + ..Default::default() + }, + BatchEntry { + file: brc20_path, + metaprotocol: Some("brc-20".to_string()), + ..Default::default() + } + ], + parent: Some(parent), + ..Default::default() + } + ); + } + + #[test] + fn batch_with_unknown_field_throws_error() { + let tempdir = TempDir::new().unwrap(); + let batch_path = tempdir.path().join("batch.yaml"); + fs::write( + &batch_path, + "mode: shared-output\ninscriptions:\n- file: meow.wav\nunknown: 1.)what", + ) + .unwrap(); + + assert!(Batchfile::load(&batch_path) + .unwrap_err() + .to_string() + .contains("unknown field `unknown`")); + } + + #[test] + fn batch_inscribe_with_parent() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(50_000)), + ]; + + let parent = inscription_id(1); + + let parent_info = ParentInfo { + destination: change(3), + id: parent, + location: SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + tx_out: TxOut { + script_pubkey: change(0).script_pubkey(), + value: 10000, + }, + }; + + let mut wallet_inscriptions = BTreeMap::new(); + wallet_inscriptions.insert(parent_info.location, parent); + + let commit_address = change(1); + let reveal_addresses = vec![recipient()]; + + let inscriptions = vec![ + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + ]; + + let mode = Mode::SharedOutput; + + let fee_rate = 4.0.try_into().unwrap(); + + let (commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint: None, + parent_info: Some(parent_info.clone()), + inscriptions, + destinations: reveal_addresses, + commit_fee_rate: fee_rate, + reveal_fee_rate: fee_rate, + no_limit: false, + reinscribe: false, + postage: Amount::from_sat(10_000), + mode, + ..Default::default() + } + .create_batch_inscription_transactions( + wallet_inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ) + .unwrap(); + + let sig_vbytes = 17; + let fee = fee_rate.fee(commit_tx.vsize() + sig_vbytes).to_sat(); + + let reveal_value = commit_tx + .output + .iter() + .map(|o| o.value) + .reduce(|acc, i| acc + i) + .unwrap(); + + assert_eq!(reveal_value, 50_000 - fee); + + let sig_vbytes = 16; + let fee = fee_rate.fee(reveal_tx.vsize() + sig_vbytes).to_sat(); + + assert_eq!(fee, commit_tx.output[0].value - reveal_tx.output[1].value,); + assert_eq!( + reveal_tx.output[0].script_pubkey, + parent_info.destination.script_pubkey() + ); + assert_eq!(reveal_tx.output[0].value, parent_info.tx_out.value); + pretty_assert_eq!( + reveal_tx.input[0], + TxIn { + previous_output: parent_info.location.outpoint, + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + ..Default::default() + } + ); + } + + #[test] + fn batch_inscribe_with_parent_not_enough_cardinals_utxos_fails() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(20_000)), + ]; + + let parent = inscription_id(1); + + let parent_info = ParentInfo { + destination: change(3), + id: parent, + location: SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + tx_out: TxOut { + script_pubkey: change(0).script_pubkey(), + value: 10000, + }, + }; + + let mut wallet_inscriptions = BTreeMap::new(); + wallet_inscriptions.insert(parent_info.location, parent); + + let inscriptions = vec![ + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + ]; + + let commit_address = change(1); + let reveal_addresses = vec![recipient()]; + + let error = Batch { + satpoint: None, + parent_info: Some(parent_info.clone()), + inscriptions, + destinations: reveal_addresses, + commit_fee_rate: 4.0.try_into().unwrap(), + reveal_fee_rate: 4.0.try_into().unwrap(), + no_limit: false, + reinscribe: false, + postage: Amount::from_sat(10_000), + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + wallet_inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ) + .unwrap_err() + .to_string(); + + assert!(error.contains( + "wallet does not contain enough cardinal UTXOs, please add additional funds to wallet." + )); + } + + #[test] + #[should_panic( + expected = "invariant: destination addresses and number of inscriptions doesn't match" + )] + fn batch_inscribe_with_inconsistent_reveal_addresses_panics() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(80_000)), + ]; + + let parent = inscription_id(1); + + let parent_info = ParentInfo { + destination: change(3), + id: parent, + location: SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + tx_out: TxOut { + script_pubkey: change(0).script_pubkey(), + value: 10000, + }, + }; + + let mut wallet_inscriptions = BTreeMap::new(); + wallet_inscriptions.insert(parent_info.location, parent); + + let inscriptions = vec![ + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + ]; + + let commit_address = change(1); + let reveal_addresses = vec![recipient(), recipient()]; + + let _ = Batch { + satpoint: None, + parent_info: Some(parent_info.clone()), + inscriptions, + destinations: reveal_addresses, + commit_fee_rate: 4.0.try_into().unwrap(), + reveal_fee_rate: 4.0.try_into().unwrap(), + no_limit: false, + reinscribe: false, + postage: Amount::from_sat(10_000), + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + wallet_inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ); + } + + #[test] + fn batch_inscribe_over_max_standard_tx_weight() { + let utxos = vec![(outpoint(1), Amount::from_sat(50 * COIN_VALUE))]; + + let wallet_inscriptions = BTreeMap::new(); + + let inscriptions = vec![ + inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize / 3]), + inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize / 3]), + inscription("text/plain", [0; MAX_STANDARD_TX_WEIGHT as usize / 3]), + ]; + + let commit_address = change(1); + let reveal_addresses = vec![recipient()]; + + let error = Batch { + satpoint: None, + parent_info: None, + inscriptions, + destinations: reveal_addresses, + commit_fee_rate: 1.0.try_into().unwrap(), + reveal_fee_rate: 1.0.try_into().unwrap(), + no_limit: false, + reinscribe: false, + postage: Amount::from_sat(30_000), + mode: Mode::SharedOutput, + ..Default::default() + } + .create_batch_inscription_transactions( + wallet_inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ) + .unwrap_err() + .to_string(); + + assert!( + error.contains(&format!("reveal transaction weight greater than {MAX_STANDARD_TX_WEIGHT} (MAX_STANDARD_TX_WEIGHT): 402841")), + "{}", + error + ); + } + + #[test] + fn batch_inscribe_into_separate_outputs() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(80_000)), + ]; + + let wallet_inscriptions = BTreeMap::new(); + + let commit_address = change(1); + let reveal_addresses = vec![recipient(), recipient(), recipient()]; + + let inscriptions = vec![ + inscription("text/plain", [b'O'; 100]), + inscription("text/plain", [b'O'; 111]), + inscription("text/plain", [b'O'; 222]), + ]; + + let mode = Mode::SeparateOutputs; + + let fee_rate = 4.0.try_into().unwrap(); + + let (_commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint: None, + parent_info: None, + inscriptions, + destinations: reveal_addresses, + commit_fee_rate: fee_rate, + reveal_fee_rate: fee_rate, + no_limit: false, + reinscribe: false, + postage: Amount::from_sat(10_000), + mode, + ..Default::default() + } + .create_batch_inscription_transactions( + wallet_inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ) + .unwrap(); + + assert_eq!(reveal_tx.output.len(), 3); + assert!(reveal_tx + .output + .iter() + .all(|output| output.value == TARGET_POSTAGE.to_sat())); + } + + #[test] + fn batch_inscribe_into_separate_outputs_with_parent() { + let utxos = vec![ + (outpoint(1), Amount::from_sat(10_000)), + (outpoint(2), Amount::from_sat(50_000)), + ]; + + let parent = inscription_id(1); + + let parent_info = ParentInfo { + destination: change(3), + id: parent, + location: SatPoint { + outpoint: outpoint(1), + offset: 0, + }, + tx_out: TxOut { + script_pubkey: change(0).script_pubkey(), + value: 10000, + }, + }; + + let mut wallet_inscriptions = BTreeMap::new(); + wallet_inscriptions.insert(parent_info.location, parent); + + let commit_address = change(1); + let reveal_addresses = vec![recipient(), recipient(), recipient()]; + + let inscriptions = vec![ + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + InscriptionTemplate { + parent: Some(parent), + ..Default::default() + } + .into(), + ]; + + let mode = Mode::SeparateOutputs; + + let fee_rate = 4.0.try_into().unwrap(); + + let (commit_tx, reveal_tx, _private_key, _) = Batch { + satpoint: None, + parent_info: Some(parent_info.clone()), + inscriptions, + destinations: reveal_addresses, + commit_fee_rate: fee_rate, + reveal_fee_rate: fee_rate, + no_limit: false, + reinscribe: false, + postage: Amount::from_sat(10_000), + mode, + ..Default::default() + } + .create_batch_inscription_transactions( + wallet_inscriptions, + Chain::Signet, + BTreeSet::new(), + BTreeSet::new(), + utxos.into_iter().collect(), + [commit_address, change(2)], + ) + .unwrap(); + + assert_eq!( + parent, + ParsedEnvelope::from_transaction(&reveal_tx)[0] + .payload + .parent() + .unwrap() + ); + assert_eq!( + parent, + ParsedEnvelope::from_transaction(&reveal_tx)[1] + .payload + .parent() + .unwrap() + ); + + let sig_vbytes = 17; + let fee = fee_rate.fee(commit_tx.vsize() + sig_vbytes).to_sat(); + + let reveal_value = commit_tx + .output + .iter() + .map(|o| o.value) + .reduce(|acc, i| acc + i) + .unwrap(); + + assert_eq!(reveal_value, 50_000 - fee); + + assert_eq!( + reveal_tx.output[0].script_pubkey, + parent_info.destination.script_pubkey() + ); + assert_eq!(reveal_tx.output[0].value, parent_info.tx_out.value); + pretty_assert_eq!( + reveal_tx.input[0], + TxIn { + previous_output: parent_info.location.outpoint, + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + ..Default::default() + } + ); + } + + #[test] + fn example_batchfile_deserializes_successfully() { + Batchfile::load(Path::new("batch.yaml")).unwrap(); + } + + #[test] + fn flags_conflict_with_batch() { + for (flag, value) in [ + ("--file", Some("foo")), + ( + "--destination", + Some("tb1qsgx55dp6gn53tsmyjjv4c2ye403hgxynxs0dnm"), + ), + ("--cbor-metadata", Some("foo")), + ("--json-metadata", Some("foo")), + ( + "--satpoint", + Some("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0:0"), + ), + ("--reinscribe", None), + ("--metaprotocol", Some("foo")), + ( + "--parent", + Some("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33bi0"), + ), + ] { + let mut args = vec![ + "ord", + "wallet", + "inscribe", + "--fee-rate", + "1", + "--batch", + "foo.yaml", + flag, + ]; + + if let Some(value) = value { + args.push(value); + } + + assert!(Arguments::try_parse_from(args) + .unwrap_err() + .to_string() + .contains("the argument '--batch ' cannot be used with")); + } + } + + #[test] + fn batch_or_file_is_required() { + assert!( + Arguments::try_parse_from(["ord", "wallet", "inscribe", "--fee-rate", "1",]) + .unwrap_err() + .to_string() + .contains("error: the following required arguments were not provided:\n <--file |--batch >") + ); + } + + #[test] + fn satpoint_and_sat_flags_conflict() { + assert_regex_match!( + Arguments::try_parse_from([ + "ord", + "--index-sats", + "wallet", + "inscribe", + "--sat", + "50000000000", + "--satpoint", + "038112028c55f3f77cc0b8b413df51f70675f66be443212da0642b7636f68a00:1:0", + "--file", + "baz", + ]) + .unwrap_err() + .to_string(), + ".*--sat.*cannot be used with.*--satpoint.*" + ); + } +} diff --git a/src/subcommand/wallet/inscriptions.rs b/src/subcommand/wallet/inscriptions.rs new file mode 100644 index 0000000000..4113d27067 --- /dev/null +++ b/src/subcommand/wallet/inscriptions.rs @@ -0,0 +1,37 @@ +use super::*; + +#[derive(Serialize, Deserialize)] +pub struct Output { + pub inscription: InscriptionId, + pub location: SatPoint, + pub explorer: String, + pub postage: u64, +} + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let unspent_outputs = wallet.get_unspent_outputs()?; + + let inscriptions = wallet.get_inscriptions()?; + + let explorer = match wallet.chain() { + Chain::Mainnet => "https://ordinals.com/inscription/", + Chain::Regtest => "http://localhost/inscription/", + Chain::Signet => "https://signet.ordinals.com/inscription/", + Chain::Testnet => "https://testnet.ordinals.com/inscription/", + }; + + let mut output = Vec::new(); + + for (location, inscription) in inscriptions { + if let Some(postage) = unspent_outputs.get(&location.outpoint) { + output.push(Output { + location, + inscription, + explorer: format!("{explorer}{inscription}"), + postage: postage.to_sat(), + }) + } + } + + Ok(Some(Box::new(output))) +} diff --git a/src/subcommand/wallet/outputs.rs b/src/subcommand/wallet/outputs.rs new file mode 100644 index 0000000000..121a6e7de6 --- /dev/null +++ b/src/subcommand/wallet/outputs.rs @@ -0,0 +1,19 @@ +use super::*; + +#[derive(Serialize, Deserialize)] +pub struct Output { + pub output: OutPoint, + pub amount: u64, +} + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let mut outputs = Vec::new(); + for (output, amount) in wallet.get_unspent_outputs()? { + outputs.push(Output { + output, + amount: amount.to_sat(), + }); + } + + Ok(Some(Box::new(outputs))) +} diff --git a/src/subcommand/wallet/receive.rs b/src/subcommand/wallet/receive.rs new file mode 100644 index 0000000000..669540f6db --- /dev/null +++ b/src/subcommand/wallet/receive.rs @@ -0,0 +1,14 @@ +use super::*; + +#[derive(Deserialize, Serialize)] +pub struct Output { + pub address: Address, +} + +pub(crate) fn run(wallet: Wallet) -> SubcommandResult { + let address = wallet + .bitcoin_client()? + .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))?; + + Ok(Some(Box::new(Output { address }))) +} diff --git a/src/subcommand/wallet/restore.rs b/src/subcommand/wallet/restore.rs new file mode 100644 index 0000000000..9513594f9b --- /dev/null +++ b/src/subcommand/wallet/restore.rs @@ -0,0 +1,21 @@ +use super::*; + +#[derive(Debug, Parser)] +pub(crate) struct Restore { + #[arg(help = "Restore wallet from ")] + mnemonic: Mnemonic, + #[arg( + long, + default_value = "", + help = "Use when deriving wallet" + )] + pub(crate) passphrase: String, +} + +impl Restore { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + wallet.initialize(self.mnemonic.to_seed(self.passphrase))?; + + Ok(None) + } +} diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs new file mode 100644 index 0000000000..35d3ea5584 --- /dev/null +++ b/src/subcommand/wallet/sats.rs @@ -0,0 +1,321 @@ +use super::*; + +#[derive(Debug, Parser)] +pub(crate) struct Sats { + #[arg( + long, + help = "Find satoshis listed in first column of tab-separated value file ." + )] + tsv: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct OutputTsv { + pub sat: String, + pub output: OutPoint, +} + +#[derive(Serialize, Deserialize)] +pub struct OutputRare { + pub sat: Sat, + pub output: OutPoint, + pub offset: u64, + pub rarity: Rarity, +} + +impl Sats { + pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { + ensure!( + wallet.check_sat_index()?, + "sats requires index created with `--index-sats` flag" + ); + + let utxos = wallet.get_output_sat_ranges()?; + + if let Some(path) = &self.tsv { + let mut output = Vec::new(); + for (outpoint, sat) in sats_from_tsv( + utxos, + &fs::read_to_string(path) + .with_context(|| format!("I/O error reading `{}`", path.display()))?, + )? { + output.push(OutputTsv { + sat: sat.into(), + output: outpoint, + }); + } + Ok(Some(Box::new(output))) + } else { + let mut output = Vec::new(); + for (outpoint, sat, offset, rarity) in rare_sats(utxos) { + output.push(OutputRare { + sat, + output: outpoint, + offset, + rarity, + }); + } + Ok(Some(Box::new(output))) + } + } +} + +fn rare_sats(utxos: Vec<(OutPoint, Vec<(u64, u64)>)>) -> Vec<(OutPoint, Sat, u64, Rarity)> { + utxos + .into_iter() + .flat_map(|(outpoint, sat_ranges)| { + let mut offset = 0; + sat_ranges.into_iter().filter_map(move |(start, end)| { + let sat = Sat(start); + let rarity = sat.rarity(); + let start_offset = offset; + offset += end - start; + if rarity > Rarity::Common { + Some((outpoint, sat, start_offset, rarity)) + } else { + None + } + }) + }) + .collect() +} + +fn sats_from_tsv( + utxos: Vec<(OutPoint, Vec<(u64, u64)>)>, + tsv: &str, +) -> Result> { + let mut needles = Vec::new(); + for (i, line) in tsv.lines().enumerate() { + if line.is_empty() || line.starts_with('#') { + continue; + } + + if let Some(value) = line.split('\t').next() { + let sat = Sat::from_str(value).map_err(|err| { + anyhow!( + "failed to parse sat from string \"{value}\" on line {}: {err}", + i + 1, + ) + })?; + + needles.push((sat, value)); + } + } + needles.sort(); + + let mut haystacks = utxos + .into_iter() + .flat_map(|(outpoint, ranges)| { + ranges + .into_iter() + .map(move |(start, end)| (start, end, outpoint)) + }) + .collect::>(); + haystacks.sort(); + + let mut i = 0; + let mut j = 0; + let mut results = Vec::new(); + while i < needles.len() && j < haystacks.len() { + let (needle, value) = needles[i]; + let (start, end, outpoint) = haystacks[j]; + + if needle >= start && needle < end { + results.push((outpoint, value)); + } + + if needle >= end { + j += 1; + } else { + i += 1; + } + } + + Ok(results) +} + +#[cfg(test)] +mod tests { + use {super::*, std::fmt::Write}; + + #[test] + fn identify_no_rare_sats() { + assert_eq!( + rare_sats(vec![( + outpoint(1), + vec![(51 * COIN_VALUE, 100 * COIN_VALUE), (1234, 5678)], + )]), + Vec::new() + ) + } + + #[test] + fn identify_one_rare_sat() { + assert_eq!( + rare_sats(vec![( + outpoint(1), + vec![(10, 80), (50 * COIN_VALUE, 100 * COIN_VALUE)], + )]), + vec![(outpoint(1), Sat(50 * COIN_VALUE), 70, Rarity::Uncommon)] + ) + } + + #[test] + fn identify_two_rare_sats() { + assert_eq!( + rare_sats(vec![( + outpoint(1), + vec![(0, 100), (1050000000000000, 1150000000000000)], + )]), + vec![ + (outpoint(1), Sat(0), 0, Rarity::Mythic), + (outpoint(1), Sat(1050000000000000), 100, Rarity::Epic) + ] + ) + } + + #[test] + fn identify_rare_sats_in_different_outpoints() { + assert_eq!( + rare_sats(vec![ + (outpoint(1), vec![(50 * COIN_VALUE, 55 * COIN_VALUE)]), + (outpoint(2), vec![(100 * COIN_VALUE, 111 * COIN_VALUE)],), + ]), + vec![ + (outpoint(1), Sat(50 * COIN_VALUE), 0, Rarity::Uncommon), + (outpoint(2), Sat(100 * COIN_VALUE), 0, Rarity::Uncommon) + ] + ) + } + + #[test] + fn identify_from_tsv_none() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "1\n").unwrap(), + Vec::new() + ) + } + + #[test] + fn identify_from_tsv_single() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n").unwrap(), + vec![(outpoint(1), "0"),] + ) + } + + #[test] + fn identify_from_tsv_two_in_one_range() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 2)])], "0\n1\n").unwrap(), + vec![(outpoint(1), "0"), (outpoint(1), "1"),] + ) + } + + #[test] + fn identify_from_tsv_out_of_order_tsv() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 2)])], "1\n0\n").unwrap(), + vec![(outpoint(1), "0"), (outpoint(1), "1"),] + ) + } + + #[test] + fn identify_from_tsv_out_of_order_ranges() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(1, 2), (0, 1)])], "1\n0\n").unwrap(), + vec![(outpoint(1), "0"), (outpoint(1), "1"),] + ) + } + + #[test] + fn identify_from_tsv_two_in_two_ranges() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1), (1, 2)])], "0\n1\n").unwrap(), + vec![(outpoint(1), "0"), (outpoint(1), "1"),] + ) + } + + #[test] + fn identify_from_tsv_two_in_two_outputs() { + assert_eq!( + sats_from_tsv( + vec![(outpoint(1), vec![(0, 1)]), (outpoint(2), vec![(1, 2)])], + "0\n1\n" + ) + .unwrap(), + vec![(outpoint(1), "0"), (outpoint(2), "1"),] + ) + } + + #[test] + fn identify_from_tsv_ignores_extra_columns() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\t===\n").unwrap(), + vec![(outpoint(1), "0"),] + ) + } + + #[test] + fn identify_from_tsv_ignores_empty_lines() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n\n\n").unwrap(), + vec![(outpoint(1), "0"),] + ) + } + + #[test] + fn identify_from_tsv_ignores_comments() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n#===\n").unwrap(), + vec![(outpoint(1), "0"),] + ) + } + + #[test] + fn parse_error_reports_line_and_value() { + assert_eq!( + sats_from_tsv(vec![(outpoint(1), vec![(0, 1)])], "0\n===\n") + .unwrap_err() + .to_string(), + "failed to parse sat from string \"===\" on line 2: invalid digit found in string", + ) + } + + #[test] + fn identify_from_tsv_is_fast() { + let mut start = 0; + let mut utxos = Vec::new(); + let mut results = Vec::new(); + for i in 0..16 { + let mut ranges = Vec::new(); + let outpoint = outpoint(i); + for _ in 0..100 { + let end = start + 50 * COIN_VALUE; + ranges.push((start, end)); + for j in 0..50 { + results.push((outpoint, start + j * COIN_VALUE)); + } + start = end; + } + utxos.push((outpoint, ranges)); + } + + let mut tsv = String::new(); + for i in 0..start / COIN_VALUE { + writeln!(tsv, "{}", i * COIN_VALUE).expect("writing to string should succeed"); + } + + let start = Instant::now(); + assert_eq!( + sats_from_tsv(utxos, &tsv) + .unwrap() + .into_iter() + .map(|(outpoint, s)| (outpoint, s.parse().unwrap())) + .collect::>(), + results + ); + + assert!(Instant::now() - start < Duration::from_secs(10)); + } +} diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs new file mode 100644 index 0000000000..15955b516e --- /dev/null +++ b/src/subcommand/wallet/send.rs @@ -0,0 +1,261 @@ +use {super::*, crate::wallet::transaction_builder::Target}; + +#[derive(Debug, Parser)] +pub(crate) struct Send { + address: Address, + outgoing: Outgoing, + #[arg(long, help = "Use fee rate of sats/vB")] + fee_rate: FeeRate, + #[arg( + long, + help = "Target amount of postage to include with sent inscriptions. Default `10000sat`" + )] + pub(crate) postage: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct Output { + pub transaction: Txid, +} + +impl Send { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + let address = self + .address + .clone() + .require_network(wallet.chain().network())?; + + let unspent_outputs = wallet.get_unspent_outputs()?; + + let locked_outputs = wallet.get_locked_outputs()?; + + let inscriptions = wallet.get_inscriptions()?; + + let runic_outputs = wallet.get_runic_outputs()?; + + let satpoint = match self.outgoing { + Outgoing::Amount(amount) => { + Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; + let transaction = Self::send_amount(&wallet, amount, address, self.fee_rate)?; + return Ok(Some(Box::new(Output { transaction }))); + } + Outgoing::InscriptionId(id) => wallet.get_inscription_satpoint(id)?, + Outgoing::Rune { decimal, rune } => { + let transaction = Self::send_runes( + address, + decimal, + self.fee_rate, + inscriptions, + rune, + runic_outputs, + unspent_outputs, + &wallet, + )?; + return Ok(Some(Box::new(Output { transaction }))); + } + Outgoing::SatPoint(satpoint) => { + for inscription_satpoint in inscriptions.keys() { + if satpoint == *inscription_satpoint { + bail!("inscriptions must be sent by inscription ID"); + } + } + + ensure!( + !runic_outputs.contains(&satpoint.outpoint), + "runic outpoints may not be sent by satpoint" + ); + + satpoint + } + }; + + let change = [wallet.get_change_address()?, wallet.get_change_address()?]; + + let postage = if let Some(postage) = self.postage { + Target::ExactPostage(postage) + } else { + Target::Postage + }; + + let unsigned_transaction = TransactionBuilder::new( + satpoint, + inscriptions, + unspent_outputs, + locked_outputs, + runic_outputs, + address.clone(), + change, + self.fee_rate, + postage, + ) + .build_transaction()?; + + let signed_tx = wallet + .bitcoin_client()? + .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? + .hex; + + let txid = wallet.bitcoin_client()?.send_raw_transaction(&signed_tx)?; + + Ok(Some(Box::new(Output { transaction: txid }))) + } + + fn lock_non_cardinal_outputs( + wallet: &Wallet, + inscriptions: &BTreeMap, + runic_outputs: &BTreeSet, + unspent_outputs: BTreeMap, + ) -> Result { + let all_inscription_outputs = inscriptions + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + let locked_outputs = unspent_outputs + .keys() + .filter(|utxo| all_inscription_outputs.contains(utxo)) + .chain(runic_outputs.iter()) + .cloned() + .collect::>(); + + if !wallet.bitcoin_client()?.lock_unspent(&locked_outputs)? { + bail!("failed to lock UTXOs"); + } + + Ok(()) + } + + fn send_amount( + wallet: &Wallet, + amount: Amount, + address: Address, + fee_rate: FeeRate, + ) -> Result { + Ok(wallet.bitcoin_client()?.call( + "sendtoaddress", + &[ + address.to_string().into(), // 1. address + amount.to_btc().into(), // 2. amount + serde_json::Value::Null, // 3. comment + serde_json::Value::Null, // 4. comment_to + serde_json::Value::Null, // 5. subtractfeefromamount + serde_json::Value::Null, // 6. replaceable + serde_json::Value::Null, // 7. conf_target + serde_json::Value::Null, // 8. estimate_mode + serde_json::Value::Null, // 9. avoid_reuse + fee_rate.n().into(), // 10. fee_rate + ], + )?) + } + + fn send_runes( + address: Address, + decimal: Decimal, + fee_rate: FeeRate, + inscriptions: BTreeMap, + spaced_rune: SpacedRune, + runic_outputs: BTreeSet, + unspent_outputs: BTreeMap, + wallet: &Wallet, + ) -> Result { + ensure!( + wallet.check_rune_index()?, + "sending runes with `ord send` requires index created with `--index-runes` flag", + ); + + Self::lock_non_cardinal_outputs(wallet, &inscriptions, &runic_outputs, unspent_outputs)?; + + let (id, entry, _parent) = wallet + .get_rune(spaced_rune.rune)? + .with_context(|| format!("rune `{}` has not been etched", spaced_rune.rune))?; + + let amount = decimal.to_amount(entry.divisibility)?; + + let inscribed_outputs = inscriptions + .keys() + .map(|satpoint| satpoint.outpoint) + .collect::>(); + + let mut input_runes = 0; + let mut input = Vec::new(); + + for output in runic_outputs { + if inscribed_outputs.contains(&output) { + continue; + } + + let balance = wallet.get_rune_balance_in_output(&output, entry.rune)?; + + if balance > 0 { + input_runes += balance; + input.push(output); + } + + if input_runes >= amount { + break; + } + } + + ensure! { + input_runes >= amount, + "insufficient `{}` balance, only {} in wallet", + spaced_rune, + Pile { + amount: input_runes, + divisibility: entry.divisibility, + symbol: entry.symbol + }, + } + + let runestone = Runestone { + edicts: vec![Edict { + amount, + id: id.into(), + output: 2, + }], + ..Default::default() + }; + + let unfunded_transaction = Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: input + .into_iter() + .map(|previous_output| TxIn { + previous_output, + script_sig: ScriptBuf::new(), + sequence: Sequence::MAX, + witness: Witness::new(), + }) + .collect(), + output: vec![ + TxOut { + script_pubkey: runestone.encipher(), + value: 0, + }, + TxOut { + script_pubkey: wallet.get_change_address()?.script_pubkey(), + value: TARGET_POSTAGE.to_sat(), + }, + TxOut { + script_pubkey: address.script_pubkey(), + value: TARGET_POSTAGE.to_sat(), + }, + ], + }; + + let unsigned_transaction = + fund_raw_transaction(&wallet.bitcoin_client()?, fee_rate, &unfunded_transaction)?; + + let signed_transaction = wallet + .bitcoin_client()? + .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? + .hex; + + Ok( + wallet + .bitcoin_client()? + .send_raw_transaction(&signed_transaction)?, + ) + } +} diff --git a/src/subcommand/wallet/transactions.rs b/src/subcommand/wallet/transactions.rs new file mode 100644 index 0000000000..f0ad7d9057 --- /dev/null +++ b/src/subcommand/wallet/transactions.rs @@ -0,0 +1,34 @@ +use super::*; + +#[derive(Debug, Parser)] +pub(crate) struct Transactions { + #[arg(long, help = "Fetch at most transactions.")] + limit: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct Output { + pub transaction: Txid, + pub confirmations: i32, +} + +impl Transactions { + pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { + let client = wallet.bitcoin_client()?; + + let mut output = Vec::new(); + for tx in client.list_transactions( + None, + Some(self.limit.unwrap_or(u16::MAX).into()), + None, + None, + )? { + output.push(Output { + transaction: tx.info.txid, + confirmations: tx.info.confirmations, + }); + } + + Ok(Some(Box::new(output))) + } +} From 6815ce4b1056f9d3114e9bb1fc34e1be4a64dc14 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 16 Jan 2024 21:49:50 +0100 Subject: [PATCH 42/74] Tests pass --- src/index.rs | 8 ++++---- src/options.rs | 2 +- src/subcommand.rs | 2 +- src/subcommand/wallet.rs | 4 +--- src/subcommand/wallet/inscribe.rs | 2 +- tests/etch.rs | 10 +++++----- tests/lib.rs | 4 ++-- tests/server.rs | 3 ++- tests/wallet/balance.rs | 2 +- tests/wallet/cardinals.rs | 2 +- tests/wallet/create.rs | 2 +- tests/wallet/inscribe.rs | 2 +- tests/wallet/inscriptions.rs | 2 +- tests/wallet/outputs.rs | 2 +- tests/wallet/receive.rs | 2 +- tests/wallet/restore.rs | 2 +- tests/wallet/sats.rs | 2 +- tests/wallet/send.rs | 2 +- tests/wallet/transactions.rs | 2 +- 19 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/index.rs b/src/index.rs index 5aaee1dc2b..4e9609b269 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3353,10 +3353,10 @@ mod tests { Arguments { options, - subcommand: Subcommand::Wallet(wallet::WalletCommand { + subcommand: Subcommand::Wallet(subcommand::wallet::WalletCommand { name: "ord".into(), no_sync: false, - subcommand: wallet::Subcommand::Create(wallet::create::Create { + subcommand: subcommand::wallet::Subcommand::Create(subcommand::wallet::create::Create { passphrase: "".into(), }), }), @@ -3384,10 +3384,10 @@ mod tests { assert_regex_match!( Arguments { options, - subcommand: Subcommand::Wallet(wallet::WalletCommand { + subcommand: Subcommand::Wallet(subcommand::wallet::WalletCommand { name: "ord".into(), no_sync: true, - subcommand: wallet::Subcommand::Balance, + subcommand: subcommand::wallet::Subcommand::Balance, }), } .run() diff --git a/src/options.rs b/src/options.rs index 884b6cee0d..fc03adb9dd 100644 --- a/src/options.rs +++ b/src/options.rs @@ -567,7 +567,7 @@ mod tests { ); } - fn parse_wallet_args(args: &str) -> (Options, wallet::WalletCommand) { + fn parse_wallet_args(args: &str) -> (Options, subcommand::wallet::WalletCommand) { match Arguments::try_parse_from(args.split_whitespace()) { Ok(arguments) => match arguments.subcommand { Subcommand::Wallet(wallet) => (arguments.options, wallet), diff --git a/src/subcommand.rs b/src/subcommand.rs index 59a429c0a9..f9d965596d 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -14,7 +14,7 @@ pub mod subsidy; pub mod supply; pub mod teleburn; pub mod traits; -mod wallet; +pub mod wallet; #[derive(Debug, Parser)] pub(crate) enum Subcommand { diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index affbb7a22c..366075d2a9 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -1,9 +1,7 @@ use { super::*, crate::wallet::{ - inscribe::{ - batch::{Batch, Batchfile, Mode}, - }, + inscribe::batch::{Batch, Batchfile, Mode}, Wallet, }, reqwest::Url, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index 896b8d627a..b811cf3f7b 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -187,7 +187,7 @@ impl Inscribe { mod tests { use { super::*, - crate::wallet::inscribe::batch::BatchEntry, + crate::wallet::inscribe::{batch::BatchEntry, ParentInfo}, bitcoin::policy::MAX_STANDARD_TX_WEIGHT, serde_yaml::{Mapping, Value}, }; diff --git a/tests/etch.rs b/tests/etch.rs index 3336b6344e..f3907bc5bc 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -1,7 +1,7 @@ use { super::*, ord::{ - wallet::{balance, etch::Output}, + subcommand::wallet::{balance, etch::Output}, Rune, }, }; @@ -254,7 +254,7 @@ fn etch_does_not_select_inscribed_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 5000000000); @@ -267,7 +267,7 @@ fn etch_does_not_select_inscribed_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); @@ -306,7 +306,7 @@ fn inscribe_does_not_select_runic_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 0); @@ -404,7 +404,7 @@ fn send_inscription_does_not_select_runic_utxos() { let output = CommandBuilder::new("--regtest --index-runes wallet balance") .rpc_server(&rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); assert_eq!(output.ordinal, 10000); diff --git a/tests/lib.rs b/tests/lib.rs index 09e1422fcd..3d6cdb2926 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -56,12 +56,12 @@ macro_rules! assert_regex_match { const RUNE: u128 = 99246114928149462; type Inscribe = ord::wallet::inscribe::Output; -type Etch = ord::wallet::etch::Output; +type Etch = ord::subcommand::wallet::etch::Output; fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) .rpc_server(rpc_server) - .run_and_deserialize_output::(); + .run_and_deserialize_output::(); } fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { diff --git a/tests/server.rs b/tests/server.rs index 133932b6ce..c7792fb279 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -1,5 +1,6 @@ use { - super::*, crate::command_builder::ToArgs, ciborium::value::Integer, ord::wallet::send::Output, + super::*, crate::command_builder::ToArgs, ciborium::value::Integer, + ord::subcommand::wallet::send::Output, }; #[test] diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index 34ff5917ef..ea50fe36b8 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -1,4 +1,4 @@ -use {super::*, ord::wallet::balance::Output}; +use {super::*, ord::subcommand::wallet::balance::Output}; #[test] fn wallet_balance() { diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index 38cb7d078d..db966c39a8 100644 --- a/tests/wallet/cardinals.rs +++ b/tests/wallet/cardinals.rs @@ -1,6 +1,6 @@ use { super::*, - ord::wallet::{cardinals::CardinalUtxo, outputs::Output}, + ord::subcommand::wallet::{cardinals::CardinalUtxo, outputs::Output}, }; #[test] diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 4bec63a8c8..59dd85525c 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -1,4 +1,4 @@ -use {super::*, ord::wallet::create::Output}; +use {super::*, ord::subcommand::wallet::create::Output}; #[test] fn create() { diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index d91fe583e9..861e2a6977 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -1,6 +1,6 @@ use { super::*, - ord::wallet::{create, inscriptions, receive}, + ord::subcommand::wallet::{create, inscriptions, receive}, std::ops::Deref, }; diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 810a5992d7..4ff430510c 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -1,6 +1,6 @@ use { super::*, - ord::wallet::{inscriptions, receive, send}, + ord::subcommand::wallet::{inscriptions, receive, send}, }; #[test] diff --git a/tests/wallet/outputs.rs b/tests/wallet/outputs.rs index f79552a183..39a648e38e 100644 --- a/tests/wallet/outputs.rs +++ b/tests/wallet/outputs.rs @@ -1,4 +1,4 @@ -use {super::*, ord::wallet::outputs::Output}; +use {super::*, ord::subcommand::wallet::outputs::Output}; #[test] fn outputs() { diff --git a/tests/wallet/receive.rs b/tests/wallet/receive.rs index 7e2f6b35b3..45a8c921b7 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -1,4 +1,4 @@ -use {super::*, ord::wallet::receive}; +use {super::*, ord::subcommand::wallet::receive}; #[test] fn receive() { diff --git a/tests/wallet/restore.rs b/tests/wallet/restore.rs index ae86bee6fe..f13e601a70 100644 --- a/tests/wallet/restore.rs +++ b/tests/wallet/restore.rs @@ -1,4 +1,4 @@ -use {super::*, ord::wallet::create}; +use {super::*, ord::subcommand::wallet::create}; #[test] fn restore_generates_same_descriptors() { diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index 76cd6fd38a..ca425bd665 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -1,6 +1,6 @@ use { super::*, - ord::wallet::sats::{OutputRare, OutputTsv}, + ord::subcommand::wallet::sats::{OutputRare, OutputTsv}, }; #[test] diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 40db4342ec..5948e76a9e 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -1,6 +1,6 @@ use { super::*, - ord::wallet::{balance, create, send}, + ord::subcommand::wallet::{balance, create, send}, std::collections::BTreeMap, }; diff --git a/tests/wallet/transactions.rs b/tests/wallet/transactions.rs index 353184fd13..8f3fd4ffc9 100644 --- a/tests/wallet/transactions.rs +++ b/tests/wallet/transactions.rs @@ -1,4 +1,4 @@ -use {super::*, ord::wallet::transactions::Output}; +use {super::*, ord::subcommand::wallet::transactions::Output}; #[test] fn transactions() { From 55b091d83747e37d358100a424f48378dfd2bbf1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 16 Jan 2024 22:11:35 +0100 Subject: [PATCH 43/74] Split up batch stuff --- src/subcommand/wallet.rs | 2 +- src/subcommand/wallet/inscribe.rs | 2 +- src/wallet/inscribe.rs | 6 +- src/wallet/inscribe/batch.rs | 127 +----------------------------- src/wallet/inscribe/batchentry.rs | 23 ++++++ src/wallet/inscribe/batchfile.rs | 93 ++++++++++++++++++++++ src/wallet/inscribe/mode.rs | 12 +++ 7 files changed, 136 insertions(+), 129 deletions(-) create mode 100644 src/wallet/inscribe/batchentry.rs create mode 100644 src/wallet/inscribe/batchfile.rs create mode 100644 src/wallet/inscribe/mode.rs diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index 366075d2a9..7a980699d4 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -1,7 +1,7 @@ use { super::*, crate::wallet::{ - inscribe::batch::{Batch, Batchfile, Mode}, + inscribe::{Batch, Batchfile, Mode}, Wallet, }, reqwest::Url, diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index b811cf3f7b..2182cf8218 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -187,7 +187,7 @@ impl Inscribe { mod tests { use { super::*, - crate::wallet::inscribe::{batch::BatchEntry, ParentInfo}, + crate::wallet::inscribe::{BatchEntry, ParentInfo}, bitcoin::policy::MAX_STANDARD_TX_WEIGHT, serde_yaml::{Mapping, Value}, }; diff --git a/src/wallet/inscribe.rs b/src/wallet/inscribe.rs index b8dfd2c0b2..427a89edef 100644 --- a/src/wallet/inscribe.rs +++ b/src/wallet/inscribe.rs @@ -1,5 +1,4 @@ use { - // self::batch::{Batch, Batchfile, Mode}, super::*, bitcoin::{ blockdata::{opcodes, script}, @@ -15,7 +14,12 @@ use { wallet::transaction_builder::Target, }; +pub use {batch::Batch, batchentry::BatchEntry, batchfile::Batchfile, mode::Mode}; + pub mod batch; +pub mod batchentry; +pub mod batchfile; +pub mod mode; #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct InscriptionInfo { diff --git a/src/wallet/inscribe/batch.rs b/src/wallet/inscribe/batch.rs index 388b689f51..2615754a1a 100644 --- a/src/wallet/inscribe/batch.rs +++ b/src/wallet/inscribe/batch.rs @@ -1,6 +1,6 @@ use super::*; -pub(crate) struct Batch { +pub struct Batch { pub(crate) commit_fee_rate: FeeRate, pub(crate) destinations: Vec
, pub(crate) dry_run: bool, @@ -533,128 +533,3 @@ impl Batch { .unwrap() } } - -#[derive(PartialEq, Debug, Copy, Clone, Serialize, Deserialize, Default)] -pub(crate) enum Mode { - #[serde(rename = "same-sat")] - SameSat, - #[default] - #[serde(rename = "separate-outputs")] - SeparateOutputs, - #[serde(rename = "shared-output")] - SharedOutput, -} - -#[derive(Deserialize, Default, PartialEq, Debug, Clone)] -#[serde(deny_unknown_fields)] -pub(crate) struct BatchEntry { - pub(crate) destination: Option>, - pub(crate) file: PathBuf, - pub(crate) metadata: Option, - pub(crate) metaprotocol: Option, -} - -impl BatchEntry { - pub(crate) fn metadata(&self) -> Result>> { - Ok(match &self.metadata { - None => None, - Some(metadata) => { - let mut cbor = Vec::new(); - ciborium::into_writer(&metadata, &mut cbor)?; - Some(cbor) - } - }) - } -} - -#[derive(Deserialize, PartialEq, Debug, Clone, Default)] -#[serde(deny_unknown_fields)] -pub(crate) struct Batchfile { - pub(crate) inscriptions: Vec, - pub(crate) mode: Mode, - pub(crate) parent: Option, - pub(crate) postage: Option, - pub(crate) sat: Option, -} - -impl Batchfile { - pub(crate) fn load(path: &Path) -> Result { - let batchfile: Batchfile = serde_yaml::from_reader(File::open(path)?)?; - - if batchfile.inscriptions.is_empty() { - bail!("batchfile must contain at least one inscription"); - } - - Ok(batchfile) - } - - pub(crate) fn inscriptions( - &self, - wallet: &Wallet, - parent_value: Option, - metadata: Option>, - postage: Amount, - compress: bool, - ) -> Result<(Vec, Vec
)> { - assert!(!self.inscriptions.is_empty()); - - if self - .inscriptions - .iter() - .any(|entry| entry.destination.is_some()) - && self.mode == Mode::SharedOutput - { - return Err(anyhow!( - "individual inscription destinations cannot be set in shared-output mode" - )); - } - - if metadata.is_some() { - assert!(self - .inscriptions - .iter() - .all(|entry| entry.metadata.is_none())); - } - - let mut pointer = parent_value.unwrap_or_default(); - - let mut inscriptions = Vec::new(); - for (i, entry) in self.inscriptions.iter().enumerate() { - inscriptions.push(Inscription::from_file( - wallet.chain(), - &entry.file, - self.parent, - if i == 0 { None } else { Some(pointer) }, - entry.metaprotocol.clone(), - match &metadata { - Some(metadata) => Some(metadata.clone()), - None => entry.metadata()?, - }, - compress, - )?); - - pointer += postage.to_sat(); - } - - let destinations = match self.mode { - Mode::SharedOutput | Mode::SameSat => vec![wallet.get_change_address()?], - Mode::SeparateOutputs => self - .inscriptions - .iter() - .map(|entry| { - entry.destination.as_ref().map_or_else( - || wallet.get_change_address(), - |address| { - address - .clone() - .require_network(wallet.chain().network()) - .map_err(|e| e.into()) - }, - ) - }) - .collect::, _>>()?, - }; - - Ok((inscriptions, destinations)) - } -} diff --git a/src/wallet/inscribe/batchentry.rs b/src/wallet/inscribe/batchentry.rs new file mode 100644 index 0000000000..8aae23ec0a --- /dev/null +++ b/src/wallet/inscribe/batchentry.rs @@ -0,0 +1,23 @@ +use super::*; + +#[derive(Deserialize, Default, PartialEq, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct BatchEntry { + pub(crate) destination: Option>, + pub(crate) file: PathBuf, + pub(crate) metadata: Option, + pub(crate) metaprotocol: Option, +} + +impl BatchEntry { + pub(crate) fn metadata(&self) -> Result>> { + Ok(match &self.metadata { + None => None, + Some(metadata) => { + let mut cbor = Vec::new(); + ciborium::into_writer(&metadata, &mut cbor)?; + Some(cbor) + } + }) + } +} diff --git a/src/wallet/inscribe/batchfile.rs b/src/wallet/inscribe/batchfile.rs new file mode 100644 index 0000000000..dec6fe3a88 --- /dev/null +++ b/src/wallet/inscribe/batchfile.rs @@ -0,0 +1,93 @@ +use super::*; + +#[derive(Deserialize, PartialEq, Debug, Clone, Default)] +#[serde(deny_unknown_fields)] +pub struct Batchfile { + pub(crate) inscriptions: Vec, + pub(crate) mode: Mode, + pub(crate) parent: Option, + pub(crate) postage: Option, + pub(crate) sat: Option, +} + +impl Batchfile { + pub(crate) fn load(path: &Path) -> Result { + let batchfile: Batchfile = serde_yaml::from_reader(File::open(path)?)?; + + if batchfile.inscriptions.is_empty() { + bail!("batchfile must contain at least one inscription"); + } + + Ok(batchfile) + } + + pub(crate) fn inscriptions( + &self, + wallet: &Wallet, + parent_value: Option, + metadata: Option>, + postage: Amount, + compress: bool, + ) -> Result<(Vec, Vec
)> { + assert!(!self.inscriptions.is_empty()); + + if self + .inscriptions + .iter() + .any(|entry| entry.destination.is_some()) + && self.mode == Mode::SharedOutput + { + return Err(anyhow!( + "individual inscription destinations cannot be set in shared-output mode" + )); + } + + if metadata.is_some() { + assert!(self + .inscriptions + .iter() + .all(|entry| entry.metadata.is_none())); + } + + let mut pointer = parent_value.unwrap_or_default(); + + let mut inscriptions = Vec::new(); + for (i, entry) in self.inscriptions.iter().enumerate() { + inscriptions.push(Inscription::from_file( + wallet.chain(), + &entry.file, + self.parent, + if i == 0 { None } else { Some(pointer) }, + entry.metaprotocol.clone(), + match &metadata { + Some(metadata) => Some(metadata.clone()), + None => entry.metadata()?, + }, + compress, + )?); + + pointer += postage.to_sat(); + } + + let destinations = match self.mode { + Mode::SharedOutput | Mode::SameSat => vec![wallet.get_change_address()?], + Mode::SeparateOutputs => self + .inscriptions + .iter() + .map(|entry| { + entry.destination.as_ref().map_or_else( + || wallet.get_change_address(), + |address| { + address + .clone() + .require_network(wallet.chain().network()) + .map_err(|e| e.into()) + }, + ) + }) + .collect::, _>>()?, + }; + + Ok((inscriptions, destinations)) + } +} diff --git a/src/wallet/inscribe/mode.rs b/src/wallet/inscribe/mode.rs new file mode 100644 index 0000000000..1747d4aa95 --- /dev/null +++ b/src/wallet/inscribe/mode.rs @@ -0,0 +1,12 @@ +use super::*; + +#[derive(PartialEq, Debug, Copy, Clone, Serialize, Deserialize, Default)] +pub enum Mode { + #[serde(rename = "same-sat")] + SameSat, + #[default] + #[serde(rename = "separate-outputs")] + SeparateOutputs, + #[serde(rename = "shared-output")] + SharedOutput, +} From 257c2d8031152acb8983b5fe8b17bf02eb2ffae1 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Tue, 16 Jan 2024 23:12:57 +0100 Subject: [PATCH 44/74] Fix test --- src/wallet.rs | 2 +- src/wallet/inscribe/batchfile.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/wallet.rs b/src/wallet.rs index 20b06ab749..bc9330dacc 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -190,7 +190,7 @@ impl Wallet { pub(crate) fn inscription_exists(&self, inscription_id: InscriptionId) -> Result { Ok( - self + !self .ord_client()? .get( self diff --git a/src/wallet/inscribe/batchfile.rs b/src/wallet/inscribe/batchfile.rs index c774d27ff0..1f08dafc5c 100644 --- a/src/wallet/inscribe/batchfile.rs +++ b/src/wallet/inscribe/batchfile.rs @@ -53,6 +53,13 @@ impl Batchfile { let mut inscriptions = Vec::new(); for (i, entry) in self.inscriptions.iter().enumerate() { + if let Some(delegate) = entry.delegate { + ensure! { + wallet.inscription_exists(delegate)?, + "delegate {delegate} does not exist" + } + } + inscriptions.push(Inscription::from_file( wallet.chain(), compress, From f59744b56dbdb68c703af8dcb2d12b7a554d7dea Mon Sep 17 00:00:00 2001 From: raphjaph Date: Wed, 17 Jan 2024 00:33:09 +0100 Subject: [PATCH 45/74] Require running ord server for wallet commands --- src/index.rs | 68 ------------------- src/subcommand/preview.rs | 133 +++++++++++++++++--------------------- src/subcommand/wallet.rs | 59 +++-------------- src/wallet.rs | 2 +- tests/wallet/balance.rs | 26 ++++++++ 5 files changed, 98 insertions(+), 190 deletions(-) diff --git a/src/index.rs b/src/index.rs index 6c27263cda..2991ddaf20 100644 --- a/src/index.rs +++ b/src/index.rs @@ -3257,74 +3257,6 @@ mod tests { } } - #[test] - fn unsynced_index_fails() { - for context in Context::configurations() { - let tempdir = TempDir::new().unwrap(); - let cookie_file = tempdir.path().join("cookie"); - - fs::write(&cookie_file, "username:password").unwrap(); - - let options = Options { - chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(tempdir.path().into()), - data_dir: tempdir.path().into(), - rpc_url: context.options.rpc_url.clone(), - index_sats: true, - cookie_file: Some(cookie_file.clone()), - ..Options::default() - }; - - context.rpc_server.mine_blocks(2); - - Arguments { - options, - subcommand: Subcommand::Wallet(subcommand::wallet::WalletCommand { - name: "ord".into(), - no_sync: false, - subcommand: subcommand::wallet::Subcommand::Create(subcommand::wallet::create::Create { - passphrase: "".into(), - }), - }), - } - .run() - .unwrap(); - - let tempdir = TempDir::new().unwrap(); - let cookie_file = tempdir.path().join("cookie"); - - fs::write(&cookie_file, "username:password").unwrap(); - - let options = Options { - chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(tempdir.path().into()), - data_dir: tempdir.path().into(), - rpc_url: context.options.rpc_url.clone(), - index_sats: true, - cookie_file: Some(cookie_file.clone()), - ..Options::default() - }; - - context.rpc_server.mine_blocks(2); - - assert_regex_match!( - Arguments { - options, - subcommand: Subcommand::Wallet(subcommand::wallet::WalletCommand { - name: "ord".into(), - no_sync: true, - subcommand: subcommand::wallet::Subcommand::Balance, - }), - } - .run() - .err() - .unwrap() - .to_string(), - r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+" - ); - } - } - #[test] fn unrecognized_even_field_inscriptions_are_cursed_and_unbound() { for context in Context::configurations() { diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index e9a499111f..7646a03763 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -1,4 +1,4 @@ -use {super::*, fee_rate::FeeRate, std::sync::atomic}; +use {super::*, fee_rate::FeeRate, reqwest::Url, std::sync::atomic}; #[derive(Debug, Parser)] pub(crate) struct Preview { @@ -62,11 +62,20 @@ impl Preview { .context("failed to spawn `bitcoind`")?, ); + let server_url: Url = { + format!( + "http://127.0.0.1:{}", + TcpListener::bind("127.0.0.1:0")?.local_addr()?.port() // very hacky + ) + .parse() + .unwrap() + }; + let options = Options { chain_argument: Chain::Regtest, bitcoin_data_dir: Some(bitcoin_data_dir.clone()), data_dir: tmpdir.path().into(), - rpc_url: Some(format!("127.0.0.1:{rpc_port}")), + rpc_url: Some(format!("http://127.0.0.1:{rpc_port}")), index_sats: true, ..Options::default() }; @@ -87,7 +96,8 @@ impl Preview { options: options.clone(), subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { name: "ord".into(), - no_sync: false, + no_sync: true, + server_url: server_url.clone(), subcommand: crate::subcommand::wallet::Subcommand::Create( crate::subcommand::wallet::create::Create { passphrase: "".into(), @@ -108,24 +118,60 @@ impl Preview { rpc_client.generate_to_address(101, &address)?; - if let Some(files) = self.files { - for file in files { - let tmpdir = TempDir::new()?; + { + let options = options.clone(); + let rpc_client = options.bitcoin_rpc_client(None)?; + let address = address.clone(); + + std::thread::spawn(move || { + if let Some(blocktime) = self.blocktime { + eprintln!( + "Mining blocks every {}...", + "second".tally(blocktime.try_into().unwrap()) + ); + + let running = Arc::new(AtomicBool::new(true)); + + let handle = { + let running = running.clone(); + + std::thread::spawn(move || { + while running.load(atomic::Ordering::SeqCst) { + rpc_client.generate_to_address(1, &address).unwrap(); + thread::sleep(Duration::from_secs(blocktime)); + } + }) + }; + + Arguments { + options, + subcommand: Subcommand::Server(self.server), + } + .run() + .unwrap(); - let options = Options { - chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(bitcoin_data_dir.clone()), - data_dir: tmpdir.path().into(), - rpc_url: Some(format!("127.0.0.1:{rpc_port}")), - index_sats: true, - ..Options::default() - }; + running.store(false, atomic::Ordering::SeqCst); + + handle.join().unwrap(); + } else { + Arguments { + options, + subcommand: Subcommand::Server(self.server), + } + .run() + .unwrap(); + } + }); + } + if let Some(files) = self.files { + for file in files { Arguments { options: options.clone(), subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), no_sync: false, + server_url: server_url.clone(), subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: None, cbor_metadata: None, @@ -156,21 +202,12 @@ impl Preview { if let Some(batches) = self.batches { for batch in batches { - let tmpdir = TempDir::new()?; - - let options = Options { - chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(bitcoin_data_dir.clone()), - data_dir: tmpdir.path().into(), - rpc_url: Some(format!("127.0.0.1:{rpc_port}")), - index_sats: true, - ..Options::default() - }; Arguments { options: options.clone(), subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), no_sync: false, + server_url: server_url.clone(), subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: Some(batch), cbor_metadata: None, @@ -198,54 +235,6 @@ impl Preview { rpc_client.generate_to_address(1, &address)?; } } - - let tmpdir = TempDir::new()?; - - let options = Options { - chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(bitcoin_data_dir), - data_dir: tmpdir.path().into(), - rpc_url: Some(format!("127.0.0.1:{rpc_port}")), - index_sats: true, - ..Options::default() - }; - - if let Some(blocktime) = self.blocktime { - eprintln!( - "Mining blocks every {}...", - "second".tally(blocktime.try_into().unwrap()) - ); - - let running = Arc::new(AtomicBool::new(true)); - - let handle = { - let running = running.clone(); - - std::thread::spawn(move || { - while running.load(atomic::Ordering::SeqCst) { - rpc_client.generate_to_address(1, &address).unwrap(); - thread::sleep(Duration::from_secs(blocktime)); - } - }) - }; - - Arguments { - options, - subcommand: Subcommand::Server(self.server), - } - .run()?; - - running.store(false, atomic::Ordering::SeqCst); - - handle.join().unwrap(); - } else { - Arguments { - options, - subcommand: Subcommand::Server(self.server), - } - .run()?; - } - Ok(None) } } diff --git a/src/subcommand/wallet.rs b/src/subcommand/wallet.rs index befe5d1854..9aac669fc3 100644 --- a/src/subcommand/wallet.rs +++ b/src/subcommand/wallet.rs @@ -26,6 +26,12 @@ pub(crate) struct WalletCommand { pub(crate) name: String, #[arg(long, alias = "nosync", help = "Do not update index.")] pub(crate) no_sync: bool, + #[arg( + long, + default_value = "http://127.0.0.1:80", + help = "Use ord running at ." + )] + pub(crate) server_url: Url, #[command(subcommand)] pub(crate) subcommand: Subcommand, } @@ -61,51 +67,14 @@ pub(crate) enum Subcommand { impl WalletCommand { pub(crate) fn run(self, options: Options) -> SubcommandResult { - let index = Arc::new(Index::open(&options)?); - let handle = axum_server::Handle::new(); - LISTENERS.lock().unwrap().push(handle.clone()); - - let ord_url: Url = { - format!( - "http://127.0.0.1:{}", - TcpListener::bind("127.0.0.1:0")?.local_addr()?.port() // very hacky - ) - .parse() - .unwrap() - }; - - { - let options = options.clone(); - let ord_url = ord_url.clone(); - std::thread::spawn(move || { - crate::subcommand::server::Server { - address: ord_url.host_str().map(|a| a.to_string()), - acme_domain: vec![], - csp_origin: None, - http_port: ord_url.port(), - https_port: None, - acme_cache: None, - acme_contact: vec![], - http: true, - https: false, - redirect_http_to_https: false, - enable_json_api: true, - decompress: false, - no_sync: self.no_sync, - } - .run(options, index, handle) - .unwrap() - }); - } - let wallet = Wallet { + name: self.name.clone(), no_sync: self.no_sync, options, - ord_url, - name: self.name.clone(), + ord_url: self.server_url, }; - let result = match self.subcommand { + match self.subcommand { Subcommand::Balance => balance::run(wallet), Subcommand::Create(create) => create.run(wallet), Subcommand::Etch(etch) => etch.run(wallet), @@ -118,14 +87,6 @@ impl WalletCommand { Subcommand::Transactions(transactions) => transactions.run(wallet), Subcommand::Outputs => outputs::run(wallet), Subcommand::Cardinals => cardinals::run(wallet), - }; - - LISTENERS - .lock() - .unwrap() - .iter() - .for_each(|handle| handle.shutdown()); - - result + } } } diff --git a/src/wallet.rs b/src/wallet.rs index bc9330dacc..237a0f8ae8 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -20,7 +20,7 @@ pub mod transaction_builder; pub(crate) struct Wallet { pub(crate) name: String, pub(crate) no_sync: bool, - pub(crate) options: Options, // Only need for bitcoin_rpc_client() and chain() + pub(crate) options: Options, pub(crate) ord_url: Url, } diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index ea50fe36b8..bacda2e4b1 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -100,3 +100,29 @@ fn runic_utxos_are_deducted_from_cardinal() { } ); } +#[test] +fn unsynced_wallet_fails() { + let rpc_server = test_bitcoincore_rpc::spawn(); + + create_wallet(&rpc_server); + assert_eq!( + CommandBuilder::new("wallet balance") + .rpc_server(&rpc_server) + .run_and_deserialize_output::(), + Output { + cardinal: 0, + ordinal: 0, + runic: None, + runes: None, + total: 0, + } + ); + + inscribe(&rpc_server); + + CommandBuilder::new("wallet balance") + .rpc_server(&rpc_server) + .expected_exit_code(1) + .stderr_regex(r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+") + .run_and_extract_stdout(); +} From 0e291ab9364be932da7c4347ece02cf82f199957 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 19 Jan 2024 08:23:16 -0800 Subject: [PATCH 46/74] Got one test to pass with new setup --- src/subcommand/server.rs | 2 +- src/wallet.rs | 2 +- tests/command_builder.rs | 23 ++++++++++++++++++++++- tests/lib.rs | 29 +++++++++++++++++++++++++++++ tests/test_server.rs | 10 +++++++++- tests/wallet/balance.rs | 30 ++++++++++++++++++++++++------ 6 files changed, 86 insertions(+), 10 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 9b9638ca3b..91cde725d4 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -187,7 +187,7 @@ impl Server { log::warn!("Updating index: {error}"); } } - thread::sleep(Duration::from_millis(5000)); + // thread::sleep(Duration::from_millis(50)); // TODO: What is a good time here? }); INDEXER.lock().unwrap().replace(index_thread); diff --git a/src/wallet.rs b/src/wallet.rs index 237a0f8ae8..3e0ed07d20 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -76,7 +76,7 @@ impl Wallet { if response.text()?.parse::().unwrap() >= chain_block_count { break; } else if i == 20 { - panic!("wallet failed to synchronize to index"); + bail!("wallet failed to synchronize to index"); } thread::sleep(Duration::from_millis(25)); diff --git a/tests/command_builder.rs b/tests/command_builder.rs index 01fc621fe2..a23cad49e1 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -33,6 +33,7 @@ pub(crate) struct CommandBuilder { expected_exit_code: i32, expected_stderr: Expected, expected_stdout: Expected, + ord_server_url: Option, rpc_server_cookie_file: Option, rpc_server_url: Option, stdin: Vec, @@ -46,6 +47,7 @@ impl CommandBuilder { expected_exit_code: 0, expected_stderr: Expected::String(String::new()), expected_stdout: Expected::String(String::new()), + ord_server_url: None, rpc_server_cookie_file: None, rpc_server_url: None, stdin: Vec::new(), @@ -66,6 +68,13 @@ impl CommandBuilder { } } + pub(crate) fn ord_server(self, ord_server: &TestServer) -> Self { + Self { + ord_server_url: Some(ord_server.url()), + ..self + } + } + pub(crate) fn stdin(self, stdin: Vec) -> Self { Self { stdin, ..self } } @@ -119,6 +128,18 @@ impl CommandBuilder { ]); } + let mut args = Vec::new(); + + for arg in self.args.iter() { + args.push(arg.clone()); + if arg == "wallet" { + if let Some(ord_server_url) = &self.ord_server_url { + args.push("--server-url".to_string()); + args.push(ord_server_url.to_string()); + } + } + } + command .env("ORD_INTEGRATION_TEST", "1") .stdin(Stdio::piped()) @@ -127,7 +148,7 @@ impl CommandBuilder { .current_dir(&*self.tempdir) .arg("--data-dir") .arg(self.tempdir.path()) - .args(&self.args); + .args(&args); command } diff --git a/tests/lib.rs b/tests/lib.rs index 40b27e839a..2647c31219 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -57,6 +57,13 @@ const RUNE: u128 = 99246114928149462; type Inscribe = ord::wallet::inscribe::Output; type Etch = ord::subcommand::wallet::etch::Output; +fn create_wallet_with_ord(ord_server: &TestServer, rpc_server: &test_bitcoincore_rpc::Handle) { + CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) + .ord_server(ord_server) + .rpc_server(rpc_server) + .run_and_deserialize_output::(); +} + fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) .rpc_server(rpc_server) @@ -105,6 +112,28 @@ fn runes(rpc_server: &test_bitcoincore_rpc::Handle) -> BTreeMap .runes } +fn inscribe_with_ord( + ord_server: &TestServer, + rpc_server: &test_bitcoincore_rpc::Handle, +) -> (InscriptionId, Txid) { + rpc_server.mine_blocks(1); + + let output = CommandBuilder::new(format!( + "--chain {} wallet inscribe --fee-rate 1 --file foo.txt", + rpc_server.network() + )) + .write("foo.txt", "FOO") + .ord_server(ord_server) + .rpc_server(rpc_server) + .run_and_deserialize_output::(); + + rpc_server.mine_blocks(1); + + assert_eq!(output.inscriptions.len(), 1); + + (output.inscriptions[0].id, output.reveal) +} + fn inscribe(rpc_server: &test_bitcoincore_rpc::Handle) -> (InscriptionId, Txid) { rpc_server.mine_blocks(1); diff --git a/tests/test_server.rs b/tests/test_server.rs index b31de49acb..7003656507 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -21,6 +21,10 @@ impl TestServer { Self::spawn_with_server_args(rpc_server, ord_args, &[]) } + pub(crate) fn spawn_with_json_api(rpc_server: &test_bitcoincore_rpc::Handle) -> Self { + Self::spawn_with_server_args(rpc_server, &[], &["--enable-json-api"]) + } + pub(crate) fn spawn_with_server_args( rpc_server: &test_bitcoincore_rpc::Handle, ord_args: &[&str], @@ -125,8 +129,12 @@ impl TestServer { for i in 0.. { let response = reqwest::blocking::get(self.url().join("/blockcount").unwrap()).unwrap(); + assert_eq!(response.status(), StatusCode::OK); - if response.text().unwrap().parse::().unwrap() >= chain_block_count { + + let ord_height = response.text().unwrap().parse::().unwrap(); + + if ord_height >= chain_block_count { break; } else if i == 20 { panic!("index failed to synchronize with chain"); diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index bacda2e4b1..d97e71aac6 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -101,28 +101,46 @@ fn runic_utxos_are_deducted_from_cardinal() { ); } #[test] -fn unsynced_wallet_fails() { +fn unsynced_wallet_fails_with_unindexed_output() { let rpc_server = test_bitcoincore_rpc::spawn(); + let ord_server = TestServer::spawn_with_json_api(&rpc_server); + + rpc_server.mine_blocks(1); + + create_wallet_with_ord(&ord_server, &rpc_server); - create_wallet(&rpc_server); assert_eq!( CommandBuilder::new("wallet balance") + .ord_server(&ord_server) .rpc_server(&rpc_server) .run_and_deserialize_output::(), Output { - cardinal: 0, + cardinal: 50 * COIN_VALUE, ordinal: 0, runic: None, runes: None, - total: 0, + total: 50 * COIN_VALUE, } ); - inscribe(&rpc_server); + let no_sync_ord_server = + TestServer::spawn_with_server_args(&rpc_server, &[], &["--no-sync", "--enable-json-api"]); + + inscribe_with_ord(&ord_server, &rpc_server); CommandBuilder::new("wallet balance") + .ord_server(&no_sync_ord_server) + .rpc_server(&rpc_server) + .expected_exit_code(1) + .expected_stderr("error: wallet failed to synchronize to index\n") + .run_and_extract_stdout(); + + CommandBuilder::new("wallet --no-sync balance") + .ord_server(&no_sync_ord_server) .rpc_server(&rpc_server) .expected_exit_code(1) - .stderr_regex(r"output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+") + .stderr_regex( + r"error: output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+.*", + ) .run_and_extract_stdout(); } From 660da09c4df875f62bea5cb516ba4bbc730a79c5 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 19 Jan 2024 11:20:53 -0800 Subject: [PATCH 47/74] All unit tests pass --- src/subcommand/server.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 91cde725d4..2b813afa65 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -187,8 +187,9 @@ impl Server { log::warn!("Updating index: {error}"); } } - // thread::sleep(Duration::from_millis(50)); // TODO: What is a good time here? + thread::sleep(Duration::from_millis(500)); // TODO: What is a good time here? }); + INDEXER.lock().unwrap().replace(index_thread); let config = Arc::new(options.load_config()?); From a7c883e015b41b5ee65d2805629e3abe8c702fbb Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 19 Jan 2024 16:18:14 -0800 Subject: [PATCH 48/74] I think all integration tests pass --- src/index/testing.rs | 4 +- tests/balances.rs | 20 +- tests/command_builder.rs | 31 +- tests/decode.rs | 13 +- tests/etch.rs | 266 +++++--- tests/find.rs | 10 +- tests/index.rs | 26 +- tests/info.rs | 10 +- tests/json_api.rs | 225 ++++--- tests/lib.rs | 133 ++-- tests/list.rs | 6 +- tests/runes.rs | 42 +- tests/server.rs | 204 ++++--- tests/test_server.rs | 24 +- tests/wallet/balance.rs | 78 ++- tests/wallet/cardinals.rs | 16 +- tests/wallet/create.rs | 14 +- tests/wallet/inscribe.rs | 1112 +++++++++++++++++++++------------- tests/wallet/inscriptions.rs | 73 ++- tests/wallet/outputs.rs | 43 +- tests/wallet/receive.rs | 2 +- tests/wallet/restore.rs | 8 +- tests/wallet/sats.rs | 74 ++- tests/wallet/send.rs | 538 ++++++++++------ tests/wallet/transactions.rs | 12 +- 25 files changed, 1864 insertions(+), 1120 deletions(-) diff --git a/src/index/testing.rs b/src/index/testing.rs index 199dfe7974..04f21a594b 100644 --- a/src/index/testing.rs +++ b/src/index/testing.rs @@ -36,7 +36,7 @@ impl ContextBuilder { index.update().unwrap(); Ok(Context { - options, + _options: options, rpc_server, tempdir, index, @@ -65,7 +65,7 @@ impl ContextBuilder { } pub(crate) struct Context { - pub(crate) options: Options, + pub(crate) _options: Options, pub(crate) rpc_server: test_bitcoincore_rpc::Handle, #[allow(unused)] pub(crate) tempdir: TempDir, diff --git a/tests/balances.rs b/tests/balances.rs index 1fba7f19da..2073a4b481 100644 --- a/tests/balances.rs +++ b/tests/balances.rs @@ -7,7 +7,7 @@ fn flag_is_required() { .build(); CommandBuilder::new("--regtest balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr("error: `ord balances` requires index created with `--index-runes` flag\n") .run_and_extract_stdout(); @@ -20,7 +20,7 @@ fn no_runes() { .build(); let output = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -33,17 +33,23 @@ fn no_runes() { #[test] fn with_runes() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let a = etch(&rpc_server, Rune(RUNE)); - let b = etch(&rpc_server, Rune(RUNE + 1)); + let a = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let b = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); let output = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(); assert_eq!( diff --git a/tests/command_builder.rs b/tests/command_builder.rs index a23cad49e1..2ef347d448 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -33,9 +33,9 @@ pub(crate) struct CommandBuilder { expected_exit_code: i32, expected_stderr: Expected, expected_stdout: Expected, - ord_server_url: Option, - rpc_server_cookie_file: Option, - rpc_server_url: Option, + ord_rpc_server_url: Option, + bitcoin_rpc_server_cookie_file: Option, + bitcoin_rpc_server_url: Option, stdin: Vec, tempdir: Arc, } @@ -47,9 +47,9 @@ impl CommandBuilder { expected_exit_code: 0, expected_stderr: Expected::String(String::new()), expected_stdout: Expected::String(String::new()), - ord_server_url: None, - rpc_server_cookie_file: None, - rpc_server_url: None, + ord_rpc_server_url: None, + bitcoin_rpc_server_cookie_file: None, + bitcoin_rpc_server_url: None, stdin: Vec::new(), tempdir: Arc::new(TempDir::new().unwrap()), } @@ -60,17 +60,20 @@ impl CommandBuilder { self } - pub(crate) fn rpc_server(self, rpc_server: &test_bitcoincore_rpc::Handle) -> Self { + pub(crate) fn bitcoin_rpc_server( + self, + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, + ) -> Self { Self { - rpc_server_url: Some(rpc_server.url()), - rpc_server_cookie_file: Some(rpc_server.cookie_file()), + bitcoin_rpc_server_url: Some(bitcoin_rpc_server.url()), + bitcoin_rpc_server_cookie_file: Some(bitcoin_rpc_server.cookie_file()), ..self } } - pub(crate) fn ord_server(self, ord_server: &TestServer) -> Self { + pub(crate) fn ord_rpc_server(self, ord_rpc_server: &TestServer) -> Self { Self { - ord_server_url: Some(ord_server.url()), + ord_rpc_server_url: Some(ord_rpc_server.url()), ..self } } @@ -114,13 +117,13 @@ impl CommandBuilder { pub(crate) fn command(&self) -> Command { let mut command = Command::new(executable_path("ord")); - if let Some(rpc_server_url) = &self.rpc_server_url { + if let Some(rpc_server_url) = &self.bitcoin_rpc_server_url { command.args([ "--rpc-url", rpc_server_url, "--cookie-file", self - .rpc_server_cookie_file + .bitcoin_rpc_server_cookie_file .as_ref() .unwrap() .to_str() @@ -133,7 +136,7 @@ impl CommandBuilder { for arg in self.args.iter() { args.push(arg.clone()); if arg == "wallet" { - if let Some(ord_server_url) = &self.ord_server_url { + if let Some(ord_server_url) = &self.ord_rpc_server_url { args.push("--server-url".to_string()); args.push(ord_server_url.to_string()); } diff --git a/tests/decode.rs b/tests/decode.rs index 48b400d694..936f8f3d46 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -92,15 +92,18 @@ fn from_stdin() { #[test] fn from_core() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - let (_inscription, reveal) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (_inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new(format!("decode --txid {reveal}")) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(), RawOutput { inscriptions: vec![Envelope { diff --git a/tests/etch.rs b/tests/etch.rs index f3907bc5bc..43f9ba7d33 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -8,17 +8,21 @@ use { #[test] fn flag_is_required() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "--regtest wallet etch --rune {} --divisibility 39 --fee-rate 1 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: `ord wallet etch` requires index created with `--index-runes` flag\n") .run_and_extract_stdout(); @@ -26,20 +30,27 @@ fn flag_is_required() { #[test] fn divisibility_over_max_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 39 --fee-rate 1 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: must be equal to or less than 38\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -47,20 +58,27 @@ fn divisibility_over_max_is_an_error() { #[test] fn supply_over_max_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 0 --fee-rate 1 --supply 340282366920938463463374607431768211456 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stderr_regex(r"error: invalid value '\d+' for '--supply ': number too large to fit in target type\n.*") .expected_exit_code(2) .run_and_extract_stdout(); @@ -68,20 +86,27 @@ fn supply_over_max_is_an_error() { #[test] fn rune_below_minimum_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 0 --fee-rate 1 --supply 1000 --symbol ยข", Rune(99229755678436031 - 1), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: rune is less than minimum for next block: ZZWZRFAGQTKY < ZZWZRFAGQTKZ\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -89,18 +114,25 @@ fn rune_below_minimum_is_an_error() { #[test] fn reserved_rune_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( "--index-runes --regtest wallet etch --rune AAAAAAAAAAAAAAAAAAAAAAAAAAA --divisibility 0 --fee-rate 1 --supply 1000 --symbol ยข" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: rune `AAAAAAAAAAAAAAAAAAAAAAAAAAA` is reserved\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -108,22 +140,29 @@ fn reserved_rune_is_an_error() { #[test] fn trying_to_etch_an_existing_rune_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 0 --fee-rate 1 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: rune `AAAAAAAAAAAAA` has already been etched\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -131,24 +170,31 @@ fn trying_to_etch_an_existing_rune_is_an_error() { #[test] fn runes_can_be_etched() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new( "--index-runes --regtest wallet etch --rune Aโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขAโ€ขA --divisibility 1 --fee-rate 1 --supply 1000 --symbol ยข", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); assert_eq!( - runes(&rpc_server), + runes(&bitcoin_rpc_server), vec![( Rune(RUNE), RuneInfo { @@ -178,7 +224,8 @@ fn runes_can_be_etched() { ); let output = CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!(output.runes.unwrap()[&Rune(RUNE)], 10000); @@ -186,25 +233,32 @@ fn runes_can_be_etched() { #[test] fn etch_sets_integer_fee_rate_correctly() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 100 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let tx = rpc_server.tx(2, 1); + let tx = bitcoin_rpc_server.tx(2, 1); assert_eq!(tx.txid(), output.transaction); @@ -215,25 +269,32 @@ fn etch_sets_integer_fee_rate_correctly() { #[test] fn etch_sets_decimal_fee_rate_correctly() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 100.5 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let tx = rpc_server.tx(2, 1); + let tx = bitcoin_rpc_server.tx(2, 1); assert_eq!(tx.txid(), output.transaction); @@ -244,29 +305,38 @@ fn etch_sets_decimal_fee_rate_correctly() { #[test] fn etch_does_not_select_inscribed_utxos() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!(output.cardinal, 5000000000); CommandBuilder::new("--regtest wallet inscribe --fee-rate 0 --file foo.txt --postage 50btc") .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 0); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 0); let output = CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); @@ -276,36 +346,43 @@ fn etch_does_not_select_inscribed_utxos() { "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 1 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stderr_regex("error: JSON-RPC error: .*") .expected_exit_code(1) .run_and_extract_stdout(); - - rpc_server.mine_blocks(1); } #[test] fn inscribe_does_not_select_runic_utxos() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks_with_subsidy(1, 10000); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 0 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 0); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 0); let output = CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); @@ -314,7 +391,8 @@ fn inscribe_does_not_select_runic_utxos() { CommandBuilder::new("--regtest --index-runes wallet inscribe --fee-rate 0 --file foo.txt") .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: wallet contains no cardinal utxos\n") .run_and_extract_stdout(); @@ -322,26 +400,34 @@ fn inscribe_does_not_select_runic_utxos() { #[test] fn send_amount_does_not_select_runic_utxos() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks_with_subsidy(1, 10000); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 0 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 0); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 0); CommandBuilder::new("--regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 600sat") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error: JSON-RPC error: .*") .run_and_extract_stdout(); @@ -349,26 +435,34 @@ fn send_amount_does_not_select_runic_utxos() { #[test] fn send_satpoint_does_not_send_runic_utxos() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); - rpc_server.mine_blocks_with_subsidy(1, 10000); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); let output = CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 0 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 0); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 0); CommandBuilder::new(format!("--regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw {}:1:0", output.transaction)) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: runic outpoints may not be sent by satpoint\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -376,34 +470,43 @@ fn send_satpoint_does_not_send_runic_utxos() { #[test] fn send_inscription_does_not_select_runic_utxos() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks_with_subsidy(1, 10000); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); CommandBuilder::new( format!( "--index-runes --regtest wallet etch --rune {} --divisibility 1 --fee-rate 0 --supply 1000 --symbol ยข", Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 10000); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); let inscribe = CommandBuilder::new("--regtest --index-runes wallet inscribe --fee-rate 0 --file foo.txt") .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 0); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 0); let output = CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!(output.cardinal, 0); @@ -411,7 +514,8 @@ fn send_inscription_does_not_select_runic_utxos() { assert_eq!(output.runic, Some(10000)); CommandBuilder::new(format!("--regtest --index-runes wallet send --postage 10001sat --fee-rate 0 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw {}", inscribe.inscriptions[0].id)) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: wallet does not contain enough cardinal UTXOs, please add additional funds to wallet.\n") .expected_exit_code(1) .run_and_extract_stdout(); diff --git a/tests/find.rs b/tests/find.rs index fd3c553fee..e313debdaf 100644 --- a/tests/find.rs +++ b/tests/find.rs @@ -8,7 +8,7 @@ fn find_command_returns_satpoint_for_sat() { let rpc_server = test_bitcoincore_rpc::spawn(); assert_eq!( CommandBuilder::new("--index-sats find 0") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(), Output { satpoint: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0:0" @@ -26,7 +26,7 @@ fn find_range_command_returns_satpoints_and_ranges() { pretty_assert_eq!( CommandBuilder::new(format!("--index-sats find 0 {}", 55 * COIN_VALUE)) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(), vec![ FindRangeOutput { @@ -56,7 +56,7 @@ fn find_range_command_fails_for_unmined_sat_ranges() { 50 * COIN_VALUE, 100 * COIN_VALUE )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr("error: range has not been mined as of index height\n") .run_and_extract_stdout(); @@ -66,7 +66,7 @@ fn find_range_command_fails_for_unmined_sat_ranges() { fn unmined_sat() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("--index-sats find 5000000000") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_stderr("error: sat has not been mined as of index height\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -76,7 +76,7 @@ fn unmined_sat() { fn no_satoshi_index() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("find 0") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_stderr("error: find requires index created with `--index-sats` flag\n") .expected_exit_code(1) .run_and_extract_stdout(); diff --git a/tests/index.rs b/tests/index.rs index 663acb9f8b..6a63ecc966 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -10,7 +10,7 @@ fn run_is_an_alias_for_update() { let index_path = tempdir.path().join("foo.redb"); CommandBuilder::new(format!("--index {} index run", index_path.display())) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); assert!(index_path.is_file()) @@ -26,7 +26,7 @@ fn custom_index_path() { let index_path = tempdir.path().join("foo.redb"); CommandBuilder::new(format!("--index {} index update", index_path.display())) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); assert!(index_path.is_file()) @@ -42,13 +42,13 @@ fn re_opening_database_does_not_trigger_schema_check() { let index_path = tempdir.path().join("foo.redb"); CommandBuilder::new(format!("--index {} index update", index_path.display())) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); assert!(index_path.is_file()); CommandBuilder::new(format!("--index {} index update", index_path.display())) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); } @@ -83,18 +83,22 @@ fn index_runs_with_rpc_user_and_pass_as_env_vars() { #[test] fn export_inscription_number_to_id_tsv() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + let temp_dir = TempDir::new().unwrap(); - create_wallet(&rpc_server); - inscribe(&rpc_server); - inscribe(&rpc_server); - let (inscription, _) = inscribe(&rpc_server); + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let tsv = CommandBuilder::new("index export --tsv foo.tsv") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .temp_dir(Arc::new(temp_dir)) .run_and_extract_file("foo.tsv"); diff --git a/tests/info.rs b/tests/info.rs index 1ee0ac29da..ec0c6c3a48 100644 --- a/tests/info.rs +++ b/tests/info.rs @@ -4,7 +4,7 @@ use {super::*, ord::subcommand::index::info::TransactionsOutput}; fn json_with_satoshi_index() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("--index-sats index info") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .stdout_regex( r#"\{ "blocks_indexed": 1, @@ -38,7 +38,7 @@ fn json_with_satoshi_index() { fn json_without_satoshi_index() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("index info") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .stdout_regex( r#"\{ "blocks_indexed": 1, @@ -80,7 +80,7 @@ fn transactions() { "--index {} index info --transactions", index_path.display() )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>() .is_empty()); @@ -90,7 +90,7 @@ fn transactions() { "--index {} index info --transactions", index_path.display() )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].start, 0); @@ -103,7 +103,7 @@ fn transactions() { "--index {} index info --transactions", index_path.display() )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[1].start, 1); diff --git a/tests/json_api.rs b/tests/json_api.rs index 65942f7a5f..754947971b 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -2,10 +2,11 @@ use {super::*, bitcoin::BlockHash}; #[test] fn get_sat_without_sat_index() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let response = TestServer::spawn_with_server_args(&rpc_server, &[], &["--enable-json-api"]) - .json_request("/sat/2099999997689999"); + let response = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]) + .json_request("/sat/2099999997689999"); assert_eq!(response.status(), StatusCode::OK); @@ -37,15 +38,19 @@ fn get_sat_without_sat_index() { #[test] fn get_sat_with_inscription_and_sat_index() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); - let (inscription_id, reveal) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let response = - TestServer::spawn_with_server_args(&rpc_server, &["--index-sats"], &["--enable-json-api"]) - .json_request(format!("/sat/{}", 50 * COIN_VALUE)); + let (inscription_id, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + let response = ord_rpc_server.json_request(format!("/sat/{}", 50 * COIN_VALUE)); assert_eq!(response.status(), StatusCode::OK); @@ -74,31 +79,37 @@ fn get_sat_with_inscription_and_sat_index() { #[test] fn get_sat_with_inscription_on_common_sat_and_more_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - inscribe(&rpc_server); + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); let Inscribe { reveal, .. } = CommandBuilder::new(format!( "wallet inscribe --satpoint {}:0:1 --fee-rate 1 --file foo.txt", txid )) .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); + let inscription_id = InscriptionId { txid: reveal, index: 0, }; - let response = - TestServer::spawn_with_server_args(&rpc_server, &["--index-sats"], &["--enable-json-api"]) - .json_request(format!("/sat/{}", 3 * 50 * COIN_VALUE + 1)); + let response = ord_rpc_server.json_request(format!("/sat/{}", 3 * 50 * COIN_VALUE + 1)); assert_eq!(response.status(), StatusCode::OK); @@ -127,15 +138,19 @@ fn get_sat_with_inscription_on_common_sat_and_more_inscriptions() { #[test] fn get_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); - let (inscription_id, reveal) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let response = - TestServer::spawn_with_server_args(&rpc_server, &["--index-sats"], &["--enable-json-api"]) - .json_request(format!("/inscription/{}", inscription_id)); + let (inscription_id, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + let response = ord_rpc_server.json_request(format!("/inscription/{}", inscription_id)); assert_eq!(response.status(), StatusCode::OK); @@ -170,9 +185,15 @@ fn get_inscription() { #[test] fn get_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); let witness = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); @@ -180,11 +201,11 @@ fn get_inscriptions() { // Create 150 inscriptions for i in 0..50 { - rpc_server.mine_blocks(1); - rpc_server.mine_blocks(1); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let txid = rpc_server.broadcast_tx(TransactionTemplate { + let txid = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[ (i * 3 + 1, 0, 0, witness.clone()), (i * 3 + 2, 0, 0, witness.clone()), @@ -198,12 +219,9 @@ fn get_inscriptions() { inscriptions.push(InscriptionId { txid, index: 2 }); } - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let server = - TestServer::spawn_with_server_args(&rpc_server, &["--index-sats"], &["--enable-json-api"]); - - let response = server.json_request("/inscriptions"); + let response = ord_rpc_server.json_request("/inscriptions"); assert_eq!(response.status(), StatusCode::OK); let inscriptions_json: InscriptionsJson = serde_json::from_str(&response.text().unwrap()).unwrap(); @@ -212,7 +230,7 @@ fn get_inscriptions() { assert!(inscriptions_json.more); assert_eq!(inscriptions_json.page_index, 0); - let response = server.json_request("/inscriptions/1"); + let response = ord_rpc_server.json_request("/inscriptions/1"); assert_eq!(response.status(), StatusCode::OK); let inscriptions_json: InscriptionsJson = serde_json::from_str(&response.text().unwrap()).unwrap(); @@ -224,14 +242,21 @@ fn get_inscriptions() { #[test] fn get_inscriptions_in_block() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats", "--first-inscription-height", "0"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); - rpc_server.mine_blocks(10); + bitcoin_rpc_server.mine_blocks(10); let envelope = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); - let txid = rpc_server.broadcast_tx(TransactionTemplate { + let txid = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[ (1, 0, 0, envelope.clone()), (2, 0, 0, envelope.clone()), @@ -240,30 +265,24 @@ fn get_inscriptions_in_block() { ..Default::default() }); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let _ = rpc_server.broadcast_tx(TransactionTemplate { + let _ = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(4, 0, 0, envelope.clone()), (5, 0, 0, envelope.clone())], ..Default::default() }); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let _ = rpc_server.broadcast_tx(TransactionTemplate { + let _ = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[(6, 0, 0, envelope.clone())], ..Default::default() }); - rpc_server.mine_blocks(1); - - let server = TestServer::spawn_with_server_args( - &rpc_server, - &["--index-sats", "--first-inscription-height", "0"], - &["--enable-json-api"], - ); + bitcoin_rpc_server.mine_blocks(1); // get all inscriptions from block 11 - let response = server.json_request(format!("/inscriptions/block/{}", 11)); + let response = ord_rpc_server.json_request(format!("/inscriptions/block/{}", 11)); assert_eq!(response.status(), StatusCode::OK); let inscriptions_json: InscriptionsJson = @@ -281,14 +300,15 @@ fn get_inscriptions_in_block() { #[test] fn get_output() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet(&rpc_server); - rpc_server.mine_blocks(3); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + bitcoin_rpc_server.mine_blocks(3); let envelope = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); - let txid = rpc_server.broadcast_tx(TransactionTemplate { + let txid = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[ (1, 0, 0, envelope.clone()), (2, 0, 0, envelope.clone()), @@ -297,10 +317,10 @@ fn get_output() { ..Default::default() }); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let server = TestServer::spawn_with_server_args( - &rpc_server, + &bitcoin_rpc_server, &["--index-sats"], &["--no-sync", "--enable-json-api"], ); @@ -319,8 +339,11 @@ fn get_output() { .indexed ); - let server = - TestServer::spawn_with_server_args(&rpc_server, &["--index-sats"], &["--enable-json-api"]); + let server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); let response = server.json_request(format!("/output/{}:0", txid)); assert_eq!(response.status(), StatusCode::OK); @@ -353,22 +376,23 @@ fn get_output() { #[test] fn json_request_fails_when_not_enabled() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let response = - TestServer::spawn_with_args(&rpc_server, &[]).json_request("/sat/2099999997689999"); + TestServer::spawn_with_args(&bitcoin_rpc_server, &[]).json_request("/sat/2099999997689999"); assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE); } #[test] fn get_block() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let response = TestServer::spawn_with_server_args(&rpc_server, &[], &["--enable-json-api"]) - .json_request("/block/0"); + let response = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]) + .json_request("/block/0"); assert_eq!(response.status(), StatusCode::OK); @@ -392,9 +416,10 @@ fn get_block() { #[test] fn get_blocks() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - let blocks: Vec = rpc_server + let blocks: Vec = bitcoin_rpc_server .mine_blocks(101) .iter() .rev() @@ -402,8 +427,9 @@ fn get_blocks() { .map(|block| block.block_hash()) .collect(); - let response = TestServer::spawn_with_server_args(&rpc_server, &[], &["--enable-json-api"]) - .json_request("/blocks"); + ord_rpc_server.sync_server(); + + let response = ord_rpc_server.json_request("/blocks"); assert_eq!(response.status(), StatusCode::OK); @@ -425,14 +451,15 @@ fn get_blocks() { #[test] fn get_transaction() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let transaction = rpc_server.mine_blocks(1)[0].txdata[0].clone(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + let transaction = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].clone(); let txid = transaction.txid(); - let response = TestServer::spawn_with_server_args(&rpc_server, &[], &["--enable-json-api"]) - .json_request(format!("/tx/{txid}")); + let response = ord_rpc_server.json_request(format!("/tx/{txid}")); assert_eq!(response.status(), StatusCode::OK); @@ -450,21 +477,22 @@ fn get_transaction() { #[test] fn get_status() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); - - inscribe(&rpc_server); - - let response = TestServer::spawn_with_server_args( - &rpc_server, + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, &["--regtest", "--index-sats", "--index-runes"], &["--enable-json-api"], - ) - .json_request("/status"); + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + let response = ord_rpc_server.json_request("/status"); assert_eq!(response.status(), StatusCode::OK); @@ -502,26 +530,27 @@ fn get_status() { #[test] fn get_runes() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(3); - - let a = etch(&rpc_server, Rune(RUNE)); - let b = etch(&rpc_server, Rune(RUNE + 1)); - let c = etch(&rpc_server, Rune(RUNE + 2)); - - rpc_server.mine_blocks(1); - - let server = TestServer::spawn_with_server_args( - &rpc_server, + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, &["--index-runes", "--regtest"], &["--enable-json-api"], ); - let response = server.json_request(format!("/rune/{}", a.rune)); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(3); + + let a = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let b = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); + let c = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 2)); + + bitcoin_rpc_server.mine_blocks(1); + + let response = ord_rpc_server.json_request(format!("/rune/{}", a.rune)); assert_eq!(response.status(), StatusCode::OK); let rune_json: RuneJson = serde_json::from_str(&response.text().unwrap()).unwrap(); @@ -552,7 +581,7 @@ fn get_runes() { } ); - let response = server.json_request("/runes"); + let response = ord_rpc_server.json_request("/runes"); assert_eq!(response.status(), StatusCode::OK); diff --git a/tests/lib.rs b/tests/lib.rs index 2647c31219..8087dd3bc0 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -57,39 +57,47 @@ const RUNE: u128 = 99246114928149462; type Inscribe = ord::wallet::inscribe::Output; type Etch = ord::subcommand::wallet::etch::Output; -fn create_wallet_with_ord(ord_server: &TestServer, rpc_server: &test_bitcoincore_rpc::Handle) { - CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) - .ord_server(ord_server) - .rpc_server(rpc_server) - .run_and_deserialize_output::(); +fn create_wallet_new( + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, + ord_rpc_server: &TestServer, +) { + CommandBuilder::new(format!( + "--chain {} wallet create", + bitcoin_rpc_server.network() + )) + .bitcoin_rpc_server(bitcoin_rpc_server) + .ord_rpc_server(ord_rpc_server) + .run_and_deserialize_output::(); } -fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { - CommandBuilder::new(format!("--chain {} wallet create", rpc_server.network())) - .rpc_server(rpc_server) - .run_and_deserialize_output::(); -} +fn inscribe_new( + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, + ord_rpc_server: &TestServer, +) -> (InscriptionId, Txid) { + bitcoin_rpc_server.mine_blocks(1); -fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { - let mut builder = bitcoin::script::Builder::new() - .push_opcode(bitcoin::opcodes::OP_FALSE) - .push_opcode(bitcoin::opcodes::all::OP_IF); + let output = CommandBuilder::new(format!( + "--chain {} wallet inscribe --fee-rate 1 --file foo.txt", + bitcoin_rpc_server.network() + )) + .write("foo.txt", "FOO") + .bitcoin_rpc_server(bitcoin_rpc_server) + .ord_rpc_server(ord_rpc_server) + .run_and_deserialize_output::(); - for data in payload { - let mut buf = bitcoin::script::PushBytesBuf::new(); - buf.extend_from_slice(data).unwrap(); - builder = builder.push_slice(buf); - } + bitcoin_rpc_server.mine_blocks(1); - let script = builder - .push_opcode(bitcoin::opcodes::all::OP_ENDIF) - .into_script(); + assert_eq!(output.inscriptions.len(), 1); - bitcoin::Witness::from_slice(&[script.into_bytes(), Vec::new()]) + (output.inscriptions[0].id, output.reveal) } -fn etch(rpc_server: &test_bitcoincore_rpc::Handle, rune: Rune) -> Etch { - rpc_server.mine_blocks(1); +fn etch_new( + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, + ord_rpc_server: &TestServer, + rune: Rune, +) -> Etch { + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new( format!( @@ -97,59 +105,82 @@ fn etch(rpc_server: &test_bitcoincore_rpc::Handle, rune: Rune) -> Etch { rune ) ) - .rpc_server(rpc_server) + .bitcoin_rpc_server(bitcoin_rpc_server) + .ord_rpc_server(ord_rpc_server) .run_and_deserialize_output(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); output } -fn runes(rpc_server: &test_bitcoincore_rpc::Handle) -> BTreeMap { - CommandBuilder::new("--index-runes --regtest runes") - .rpc_server(rpc_server) - .run_and_deserialize_output::() - .runes +fn create_wallet(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) { + CommandBuilder::new(format!( + "--chain {} wallet create", + bitcoin_rpc_server.network() + )) + .bitcoin_rpc_server(bitcoin_rpc_server) + .run_and_deserialize_output::(); } -fn inscribe_with_ord( - ord_server: &TestServer, - rpc_server: &test_bitcoincore_rpc::Handle, -) -> (InscriptionId, Txid) { - rpc_server.mine_blocks(1); +fn inscribe(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) -> (InscriptionId, Txid) { + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new(format!( "--chain {} wallet inscribe --fee-rate 1 --file foo.txt", - rpc_server.network() + bitcoin_rpc_server.network() )) .write("foo.txt", "FOO") - .ord_server(ord_server) - .rpc_server(rpc_server) + .bitcoin_rpc_server(bitcoin_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); assert_eq!(output.inscriptions.len(), 1); (output.inscriptions[0].id, output.reveal) } -fn inscribe(rpc_server: &test_bitcoincore_rpc::Handle) -> (InscriptionId, Txid) { +fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { + let mut builder = bitcoin::script::Builder::new() + .push_opcode(bitcoin::opcodes::OP_FALSE) + .push_opcode(bitcoin::opcodes::all::OP_IF); + + for data in payload { + let mut buf = bitcoin::script::PushBytesBuf::new(); + buf.extend_from_slice(data).unwrap(); + builder = builder.push_slice(buf); + } + + let script = builder + .push_opcode(bitcoin::opcodes::all::OP_ENDIF) + .into_script(); + + bitcoin::Witness::from_slice(&[script.into_bytes(), Vec::new()]) +} + +fn etch(rpc_server: &test_bitcoincore_rpc::Handle, rune: Rune) -> Etch { rpc_server.mine_blocks(1); - let output = CommandBuilder::new(format!( - "--chain {} wallet inscribe --fee-rate 1 --file foo.txt", - rpc_server.network() - )) - .write("foo.txt", "FOO") - .rpc_server(rpc_server) - .run_and_deserialize_output::(); + let output = CommandBuilder::new( + format!( + "--index-runes --regtest wallet etch --rune {} --divisibility 0 --fee-rate 0 --supply 1000 --symbol ยข", + rune + ) + ) + .bitcoin_rpc_server(rpc_server) + .run_and_deserialize_output(); rpc_server.mine_blocks(1); - assert_eq!(output.inscriptions.len(), 1); + output +} - (output.inscriptions[0].id, output.reveal) +fn runes(rpc_server: &test_bitcoincore_rpc::Handle) -> BTreeMap { + CommandBuilder::new("--index-runes --regtest runes") + .bitcoin_rpc_server(rpc_server) + .run_and_deserialize_output::() + .runes } mod command_builder; diff --git a/tests/list.rs b/tests/list.rs index 14734a7f5f..71de89a859 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -9,7 +9,7 @@ fn output_found() { let output = CommandBuilder::new( "--index-sats list 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -34,7 +34,7 @@ fn output_not_found() { CommandBuilder::new( "--index-sats list 0000000000000000000000000000000000000000000000000000000000000000:0", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr("error: output not found\n") .run_and_extract_stdout(); @@ -44,7 +44,7 @@ fn output_not_found() { fn no_satoshi_index() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("list 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_stderr("error: list requires index created with `--index-sats` flag\n") .expected_exit_code(1) .run_and_extract_stdout(); diff --git a/tests/runes.rs b/tests/runes.rs index 729539884d..4ad789c2c1 100644 --- a/tests/runes.rs +++ b/tests/runes.rs @@ -2,12 +2,16 @@ use {super::*, ord::subcommand::runes::Output}; #[test] fn flag_is_required() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + CommandBuilder::new("--regtest runes") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: `ord runes` requires index created with `--index-runes` flag\n") .run_and_extract_stdout(); @@ -15,13 +19,13 @@ fn flag_is_required() { #[test] fn no_runes() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); assert_eq!( CommandBuilder::new("--index-runes --regtest runes") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(), Output { runes: BTreeMap::new(), @@ -31,17 +35,23 @@ fn no_runes() { #[test] fn one_rune() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let etch = etch(&rpc_server, Rune(RUNE)); + let etch = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); assert_eq!( CommandBuilder::new("--index-runes --regtest runes") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(), Output { runes: vec![( @@ -76,18 +86,24 @@ fn one_rune() { #[test] fn two_runes() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let a = etch(&rpc_server, Rune(RUNE)); - let b = etch(&rpc_server, Rune(RUNE + 1)); + let a = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let b = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); assert_eq!( CommandBuilder::new("--index-runes --regtest runes") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(), Output { runes: vec![ diff --git a/tests/server.rs b/tests/server.rs index c7792fb279..224cb90449 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -14,7 +14,7 @@ fn run() { .port(); let builder = CommandBuilder::new(format!("server --address 127.0.0.1 --http-port {port}")) - .rpc_server(&rpc_server); + .bitcoin_rpc_server(&rpc_server); let mut command = builder.command(); @@ -39,17 +39,19 @@ fn run() { #[test] fn inscription_page() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe(&rpc_server); + let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let ethereum_teleburn_address = CommandBuilder::new(format!("teleburn {inscription}")) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::() .ethereum; - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + TestServer::spawn_with_args(&bitcoin_rpc_server, &[]).assert_response_regex( format!("/inscription/{inscription}"), format!( ".*.* @@ -95,14 +97,16 @@ fn inscription_page() { #[test] fn inscription_appears_on_reveal_transaction_page() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - let (_, reveal) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + bitcoin_rpc_server.mine_blocks(1); + + TestServer::spawn_with_args(&bitcoin_rpc_server, &[]).assert_response_regex( format!("/tx/{reveal}"), format!(".*

Transaction .*

.*(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let id0 = output.inscriptions[0].id; let id1 = output.inscriptions[1].id; let reveal = output.reveal; - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/tx/{reveal}"), format!(".*

Transaction .*

.*
Output {reveal}:0.*Inscription 0.*
location
\s*
{reveal}:0:0
.*", @@ -172,15 +182,15 @@ fn inscription_page_after_send() { let txid = CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {inscription}" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stdout_regex(".*") .run_and_deserialize_output::() .transaction; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), format!( r".*

Inscription 0

.*
address
\s*
bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv
.*
location
\s*
{txid}:0:0
.*", @@ -190,17 +200,16 @@ fn inscription_page_after_send() { #[test] fn inscription_content() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let response = - TestServer::spawn_with_args(&rpc_server, &[]).request(format!("/content/{inscription}")); + let response = ord_rpc_server.request(format!("/content/{inscription}")); assert_eq!(response.status(), StatusCode::OK); assert_eq!( @@ -237,27 +246,29 @@ fn inscription_metadata() { ]); ciborium::ser::into_writer(&cbor_map, &mut encoded_metadata).unwrap(); - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let inscription_id = CommandBuilder::new( "wallet inscribe --fee-rate 1 --json-metadata metadata.json --file foo.txt", ) .write("foo.txt", "FOO") .write("metadata.json", metadata) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .inscriptions .first() .unwrap() .id; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let response = - TestServer::spawn_with_args(&rpc_server, &[]).request(format!("/r/metadata/{inscription_id}")); + let response = ord_rpc_server.request(format!("/r/metadata/{inscription_id}")); assert_eq!(response.status(), StatusCode::OK); assert_eq!( @@ -272,12 +283,14 @@ fn inscription_metadata() { #[test] fn inscriptions_page() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + ord_rpc_server.assert_response_regex( "/inscriptions", format!( ".*

All Inscriptions

@@ -291,29 +304,33 @@ fn inscriptions_page() { #[test] fn inscriptions_page_is_sorted() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); let mut regex = String::new(); for _ in 0..8 { - let (inscription, _) = inscribe(&rpc_server); + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); regex.insert_str(0, &format!(".*
.*")); } - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex("/inscriptions", ®ex); + ord_rpc_server.assert_response_regex("/inscriptions", ®ex); } #[test] fn inscriptions_page_has_next_and_previous() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - let (a, _) = inscribe(&rpc_server); - let (b, _) = inscribe(&rpc_server); - let (c, _) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + let (a, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (b, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (c, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + ord_rpc_server.assert_response_regex( format!("/inscription/{b}"), format!( ".*

Inscription 1

.* @@ -399,13 +416,13 @@ fn missing_credentials() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("--bitcoin-rpc-user foo server") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr("error: no bitcoind rpc password specified\n") .run_and_extract_stdout(); CommandBuilder::new("--bitcoin-rpc-pass bar server") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .expected_exit_code(1) .expected_stderr("error: no bitcoind rpc user specified\n") .run_and_extract_stdout(); @@ -413,41 +430,44 @@ fn missing_credentials() { #[test] fn all_endpoints_in_recursive_directory_return_json() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(2); + bitcoin_rpc_server.mine_blocks(2); - let server = TestServer::spawn_with_args(&rpc_server, &[]); + let ord_server = TestServer::spawn_with_args(&bitcoin_rpc_server, &[]); - assert_eq!(server.request("/r/blockheight").json::().unwrap(), 2); + assert_eq!( + ord_server.request("/r/blockheight").json::().unwrap(), + 2 + ); - assert_eq!(server.request("/r/blocktime").json::().unwrap(), 2); + assert_eq!(ord_server.request("/r/blocktime").json::().unwrap(), 2); assert_eq!( - server.request("/r/blockhash").json::().unwrap(), + ord_server.request("/r/blockhash").json::().unwrap(), "70a93647a8d559c7e7ff2df9bd875f5b726a2ff8ca3562003d257df5a4c47ae2" ); assert_eq!( - server.request("/r/blockhash/0").json::().unwrap(), + ord_server + .request("/r/blockhash/0") + .json::() + .unwrap(), "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" ); - assert!(server.request("/blockhash").json::().is_err()); + assert!(ord_server.request("/blockhash").json::().is_err()); - assert!(server.request("/blockhash/2").json::().is_err()); + assert!(ord_server.request("/blockhash/2").json::().is_err()); } #[test] fn sat_recursive_endpoints_without_sat_index_return_404() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); - rpc_server.mine_blocks(1); - - let server = TestServer::spawn_with_args(&rpc_server, &[""]); + let server = TestServer::spawn_with_args(&bitcoin_rpc_server, &[""]); assert_eq!( server.request("/r/sat/5000000000").status(), @@ -462,33 +482,39 @@ fn sat_recursive_endpoints_without_sat_index_return_404() { #[test] fn inscription_transactions_are_stored_with_transaction_index() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - let (_inscription, reveal) = inscribe(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-transactions"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let server = TestServer::spawn_with_args(&rpc_server, &["--index-transactions"]); + let (_inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - let coinbase = rpc_server.tx(1, 0).txid(); + let coinbase = bitcoin_rpc_server.tx(1, 0).txid(); assert_eq!( - server.request(format!("/tx/{reveal}")).status(), + ord_rpc_server.request(format!("/tx/{reveal}")).status(), StatusCode::OK, ); assert_eq!( - server.request(format!("/tx/{coinbase}")).status(), + ord_rpc_server.request(format!("/tx/{coinbase}")).status(), StatusCode::OK, ); - rpc_server.clear_state(); + bitcoin_rpc_server.clear_state(); assert_eq!( - server.request(format!("/tx/{reveal}")).status(), + ord_rpc_server.request(format!("/tx/{reveal}")).status(), StatusCode::OK, ); assert_eq!( - server.request(format!("/tx/{coinbase}")).status(), + ord_rpc_server.request(format!("/tx/{coinbase}")).status(), StatusCode::NOT_FOUND, ); } @@ -506,7 +532,7 @@ fn run_no_sync() { let tempdir = Arc::new(TempDir::new().unwrap()); let builder = CommandBuilder::new(format!("server --address 127.0.0.1 --http-port {port}",)) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .temp_dir(tempdir.clone()); let mut command = builder.command(); @@ -535,7 +561,7 @@ fn run_no_sync() { let builder = CommandBuilder::new(format!( "server --no-sync --address 127.0.0.1 --http-port {port}", )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .temp_dir(tempdir); let mut command = builder.command(); diff --git a/tests/test_server.rs b/tests/test_server.rs index 7003656507..19f8a0d5cd 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -10,29 +10,29 @@ pub(crate) struct TestServer { port: u16, #[allow(unused)] tempdir: TempDir, - rpc_url: String, + ord_rpc_url: String, } impl TestServer { pub(crate) fn spawn_with_args( - rpc_server: &test_bitcoincore_rpc::Handle, + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_args: &[&str], ) -> Self { - Self::spawn_with_server_args(rpc_server, ord_args, &[]) + Self::spawn_with_server_args(bitcoin_rpc_server, ord_args, &[]) } - pub(crate) fn spawn_with_json_api(rpc_server: &test_bitcoincore_rpc::Handle) -> Self { - Self::spawn_with_server_args(rpc_server, &[], &["--enable-json-api"]) + pub(crate) fn spawn_with_json_api(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) -> Self { + Self::spawn_with_server_args(bitcoin_rpc_server, &[], &["--enable-json-api"]) } pub(crate) fn spawn_with_server_args( - rpc_server: &test_bitcoincore_rpc::Handle, + bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_args: &[&str], - server_args: &[&str], + ord_server_args: &[&str], ) -> Self { let tempdir = TempDir::new().unwrap(); - let cookie_file = match rpc_server.network().as_str() { + let cookie_file = match bitcoin_rpc_server.network().as_str() { "mainnet" => tempdir.path().join(".cookie"), network => { fs::create_dir(tempdir.path().join(network)).unwrap(); @@ -50,11 +50,11 @@ impl TestServer { let child = Command::new(executable_path("ord")).args(format!( "--rpc-url {} --bitcoin-data-dir {} --data-dir {} {} server {} --http-port {port} --address 127.0.0.1", - rpc_server.url(), + bitcoin_rpc_server.url(), tempdir.path().display(), tempdir.path().display(), ord_args.join(" "), - server_args.join(" "), + ord_server_args.join(" "), ).to_args()) .env("ORD_INTEGRATION_TEST", "1") .current_dir(&tempdir) @@ -77,7 +77,7 @@ impl TestServer { child, tempdir, port, - rpc_url: rpc_server.url(), + ord_rpc_url: bitcoin_rpc_server.url(), } } @@ -124,7 +124,7 @@ impl TestServer { } pub(crate) fn sync_server(&self) { - let client = Client::new(&self.rpc_url, Auth::None).unwrap(); + let client = Client::new(&self.ord_rpc_url, Auth::None).unwrap(); let chain_block_count = client.get_block_count().unwrap() + 1; for i in 0.. { diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index d97e71aac6..aa91fc7536 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -2,22 +2,28 @@ use {super::*, ord::subcommand::wallet::balance::Output}; #[test] fn wallet_balance() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .cardinal, 0 ); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); assert_eq!( CommandBuilder::new("wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), Output { cardinal: 50 * COIN_VALUE, @@ -31,13 +37,17 @@ fn wallet_balance() { #[test] fn inscribed_utxos_are_deducted_from_cardinal() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), Output { cardinal: 0, @@ -48,11 +58,12 @@ fn inscribed_utxos_are_deducted_from_cardinal() { } ); - inscribe(&rpc_server); + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), Output { cardinal: 100 * COIN_VALUE - 10_000, @@ -66,15 +77,22 @@ fn inscribed_utxos_are_deducted_from_cardinal() { #[test] fn runic_utxos_are_deducted_from_cardinal() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--regtest", "--index-runes"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), Output { cardinal: 0, @@ -85,11 +103,12 @@ fn runic_utxos_are_deducted_from_cardinal() { } ); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), Output { cardinal: 100 * COIN_VALUE - 10_000, @@ -102,17 +121,17 @@ fn runic_utxos_are_deducted_from_cardinal() { } #[test] fn unsynced_wallet_fails_with_unindexed_output() { - let rpc_server = test_bitcoincore_rpc::spawn(); - let ord_server = TestServer::spawn_with_json_api(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - create_wallet_with_ord(&ord_server, &rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") - .ord_server(&ord_server) - .rpc_server(&rpc_server) + .ord_rpc_server(&ord_rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(), Output { cardinal: 50 * COIN_VALUE, @@ -123,21 +142,24 @@ fn unsynced_wallet_fails_with_unindexed_output() { } ); - let no_sync_ord_server = - TestServer::spawn_with_server_args(&rpc_server, &[], &["--no-sync", "--enable-json-api"]); + let no_sync_ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &[], + &["--no-sync", "--enable-json-api"], + ); - inscribe_with_ord(&ord_server, &rpc_server); + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet balance") - .ord_server(&no_sync_ord_server) - .rpc_server(&rpc_server) + .ord_rpc_server(&no_sync_ord_rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .expected_exit_code(1) .expected_stderr("error: wallet failed to synchronize to index\n") .run_and_extract_stdout(); CommandBuilder::new("wallet --no-sync balance") - .ord_server(&no_sync_ord_server) - .rpc_server(&rpc_server) + .ord_rpc_server(&no_sync_ord_rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .expected_exit_code(1) .stderr_regex( r"error: output in Bitcoin Core wallet but not in ord index: [[:xdigit:]]{64}:\d+.*", diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index db966c39a8..0ef82a0ebe 100644 --- a/tests/wallet/cardinals.rs +++ b/tests/wallet/cardinals.rs @@ -5,17 +5,23 @@ use { #[test] fn cardinals() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - inscribe(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let all_outputs = CommandBuilder::new("wallet outputs") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); let cardinal_outputs = CommandBuilder::new("wallet cardinals") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(all_outputs.len() - cardinal_outputs.len(), 1); diff --git a/tests/wallet/create.rs b/tests/wallet/create.rs index 59dd85525c..f0ed37ee20 100644 --- a/tests/wallet/create.rs +++ b/tests/wallet/create.rs @@ -7,7 +7,7 @@ fn create() { assert!(!rpc_server.wallets().contains("ord")); CommandBuilder::new("wallet create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert!(rpc_server.wallets().contains("ord")); @@ -16,7 +16,7 @@ fn create() { #[test] fn seed_phrases_are_twelve_words_long() { let Output { mnemonic, .. } = CommandBuilder::new("wallet create") - .rpc_server(&test_bitcoincore_rpc::spawn()) + .bitcoin_rpc_server(&test_bitcoincore_rpc::spawn()) .run_and_deserialize_output(); assert_eq!(mnemonic.word_count(), 12); @@ -27,7 +27,7 @@ fn wallet_creates_correct_mainnet_taproot_descriptor() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("wallet create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert_eq!(rpc_server.descriptors().len(), 2); @@ -48,7 +48,7 @@ fn wallet_creates_correct_test_network_taproot_descriptor() { .build(); CommandBuilder::new("--chain signet wallet create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert_eq!(rpc_server.descriptors().len(), 2); @@ -67,13 +67,13 @@ fn detect_wrong_descriptors() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("wallet create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); rpc_server.import_descriptor("wpkh([aslfjk])#a23ad2l".to_string()); CommandBuilder::new("wallet transactions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .stderr_regex( r#"error: wallet "ord" contains unexpected output descriptors, and does not appear to be an `ord` wallet, create a new wallet with `ord wallet create`\n"#, ) @@ -88,7 +88,7 @@ fn create_with_different_name() { assert!(!rpc_server.wallets().contains("inscription-wallet")); CommandBuilder::new("wallet --name inscription-wallet create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert!(rpc_server.wallets().contains("inscription-wallet")); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index 778710e0ef..b748c7c0a5 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -6,19 +6,20 @@ use { #[test] fn inscribe_creates_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - assert_eq!(rpc_server.descriptors().len(), 0); + bitcoin_rpc_server.mine_blocks(1); - create_wallet(&rpc_server); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 0); - let (inscription, _) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - assert_eq!(rpc_server.descriptors().len(), 3); + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - let request = - TestServer::spawn_with_args(&rpc_server, &[]).request(format!("/content/{inscription}")); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); + + let request = ord_rpc_server.request(format!("/content/{inscription}")); assert_eq!(request.status(), 200); assert_eq!( @@ -30,36 +31,42 @@ fn inscribe_creates_inscriptions() { #[test] fn inscribe_works_with_huge_expensive_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( "wallet inscribe --file foo.txt --satpoint {txid}:0:0 --fee-rate 10" )) .write("foo.txt", [0; 350_000]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); } #[test] fn metaprotocol_appears_on_inscription_page() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); let inscribe = CommandBuilder::new(format!( "wallet inscribe --file foo.txt --metaprotocol foo --satpoint {txid}:0:0 --fee-rate 10" )) .write("foo.txt", [0; 350_000]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", inscribe.inscriptions[0].id), r".*
metaprotocol
\s*
foo
.*", ); @@ -67,41 +74,51 @@ fn metaprotocol_appears_on_inscription_page() { #[test] fn inscribe_fails_if_bitcoin_core_is_too_old() { - let rpc_server = test_bitcoincore_rpc::builder().version(230000).build(); + let bitcoin_rpc_server = test_bitcoincore_rpc::builder().version(230000).build(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); CommandBuilder::new("wallet inscribe --file hello.txt --fee-rate 1") .write("hello.txt", "HELLOWORLD") .expected_exit_code(1) .expected_stderr("error: Bitcoin Core 24.0.0 or newer required, current version is 23.0.0\n") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_extract_stdout(); } #[test] fn inscribe_no_backup() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); - create_wallet(&rpc_server); - assert_eq!(rpc_server.descriptors().len(), 2); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + assert_eq!(bitcoin_rpc_server.descriptors().len(), 2); CommandBuilder::new("wallet inscribe --file hello.txt --no-backup --fee-rate 1") .write("hello.txt", "HELLOWORLD") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert_eq!(rpc_server.descriptors().len(), 2); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 2); } #[test] fn inscribe_unknown_file_extension() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --file pepe.xyz --fee-rate 1") .write("pepe.xyz", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex(r"error: unsupported file extension `\.xyz`, supported extensions: apng .*\n") .run_and_extract_stdout(); @@ -109,15 +126,18 @@ fn inscribe_unknown_file_extension() { #[test] fn inscribe_exceeds_chain_limit() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Signet) .build(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + + let ord_rpc_server = TestServer::spawn_with_args(&bitcoin_rpc_server, &["--signet"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("--chain signet wallet inscribe --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 1025]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr( "error: content size of 1025 bytes exceeds 1024 byte limit for signet inscriptions\n", @@ -127,45 +147,59 @@ fn inscribe_exceeds_chain_limit() { #[test] fn regtest_has_no_content_size_limit() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("--chain regtest wallet inscribe --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 1025]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stdout_regex(".*") .run_and_extract_stdout(); } #[test] fn mainnet_has_no_content_size_limit() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Bitcoin) .build(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 1025]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stdout_regex(".*") .run_and_extract_stdout(); } #[test] fn inscribe_does_not_use_inscribed_sats_as_cardinal_utxos() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks_with_subsidy(1, 100); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 100); CommandBuilder::new( "wallet inscribe --file degenerate.png --fee-rate 1" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .write("degenerate.png", [1; 100]) .expected_exit_code(1) .expected_stderr("error: wallet does not contain enough cardinal UTXOs, please add additional funds to wallet.\n") @@ -174,20 +208,23 @@ fn inscribe_does_not_use_inscribed_sats_as_cardinal_utxos() { #[test] fn refuse_to_reinscribe_sats() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let (_, reveal) = inscribe(&rpc_server); + let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks_with_subsidy(1, 100); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 100); CommandBuilder::new(format!( "wallet inscribe --satpoint {reveal}:0:0 --file hello.txt --fee-rate 1" )) .write("hello.txt", "HELLOWORLD") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr(format!("error: sat at {reveal}:0:0 already inscribed\n")) .run_and_extract_stdout(); @@ -195,10 +232,12 @@ fn refuse_to_reinscribe_sats() { #[test] fn refuse_to_inscribe_already_inscribed_utxo() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - let (inscription, reveal) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let output = OutPoint { txid: reveal, @@ -209,7 +248,8 @@ fn refuse_to_inscribe_already_inscribed_utxo() { "wallet inscribe --satpoint {output}:55555 --file hello.txt --fee-rate 1" )) .write("hello.txt", "HELLOWORLD") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr(format!( "error: utxo {output} already inscribed with inscription {inscription} on sat {output}:0\n", @@ -219,45 +259,61 @@ fn refuse_to_inscribe_already_inscribed_utxo() { #[test] fn inscribe_with_optional_satpoint_arg() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); let Inscribe { inscriptions, .. } = CommandBuilder::new(format!( "wallet inscribe --file foo.txt --satpoint {txid}:0:10000 --fee-rate 1" )) .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &["--index-sats"]).assert_response_regex( + ord_rpc_server.assert_response_regex( "/sat/5000010000", format!(".*
.*"), ); - TestServer::spawn_with_args(&rpc_server, &[]) - .assert_response_regex(format!("/content/{inscription}",), "FOO"); + ord_rpc_server.assert_response_regex(format!("/content/{inscription}",), "FOO"); } #[test] fn inscribe_with_fee_rate() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("--index-sats wallet inscribe --file degenerate.png --fee-rate 2.0") .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let tx1 = &rpc_server.mempool()[0]; + let tx1 = &bitcoin_rpc_server.mempool()[0]; let mut fee = 0; for input in &tx1.input { - fee += rpc_server + fee += bitcoin_rpc_server .get_utxo_amount(&input.previous_output) .unwrap() .to_sat(); @@ -270,7 +326,7 @@ fn inscribe_with_fee_rate() { pretty_assert_eq!(fee_rate, 2.0); - let tx2 = &rpc_server.mempool()[1]; + let tx2 = &bitcoin_rpc_server.mempool()[1]; let mut fee = 0; for input in &tx2.input { fee += &tx1.output[input.previous_output.vout as usize].value; @@ -293,21 +349,29 @@ fn inscribe_with_fee_rate() { #[test] fn inscribe_with_commit_fee_rate() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( "--index-sats wallet inscribe --file degenerate.png --commit-fee-rate 2.0 --fee-rate 1", ) .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let tx1 = &rpc_server.mempool()[0]; + let tx1 = &bitcoin_rpc_server.mempool()[0]; let mut fee = 0; for input in &tx1.input { - fee += rpc_server + fee += bitcoin_rpc_server .get_utxo_amount(&input.previous_output) .unwrap() .to_sat(); @@ -320,7 +384,7 @@ fn inscribe_with_commit_fee_rate() { pretty_assert_eq!(fee_rate, 2.0); - let tx2 = &rpc_server.mempool()[1]; + let tx2 = &bitcoin_rpc_server.mempool()[1]; let mut fee = 0; for input in &tx2.input { fee += &tx1.output[input.previous_output.vout as usize].value; @@ -336,58 +400,74 @@ fn inscribe_with_commit_fee_rate() { #[test] fn inscribe_with_wallet_named_foo() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); CommandBuilder::new("wallet --name foo create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet --name foo inscribe --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); } #[test] fn inscribe_with_dry_run_flag() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --dry-run --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert!(rpc_server.mempool().is_empty()); + assert!(bitcoin_rpc_server.mempool().is_empty()); CommandBuilder::new("wallet inscribe --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert_eq!(rpc_server.mempool().len(), 2); + assert_eq!(bitcoin_rpc_server.mempool().len(), 2); } #[test] fn inscribe_with_dry_run_flag_fees_increase() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let total_fee_dry_run = CommandBuilder::new("wallet inscribe --dry-run --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .total_fees; let total_fee_normal = CommandBuilder::new("wallet inscribe --dry-run --file degenerate.png --fee-rate 1.1") .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .total_fees; @@ -396,12 +476,17 @@ fn inscribe_with_dry_run_flag_fees_increase() { #[test] fn inscribe_to_specific_destination() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let destination = CommandBuilder::new("wallet receive") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .address; @@ -410,11 +495,12 @@ fn inscribe_to_specific_destination() { destination.clone().assume_checked() )) .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .reveal; - let reveal_tx = &rpc_server.mempool()[1]; // item 0 is the commit, item 1 is the reveal. + let reveal_tx = &bitcoin_rpc_server.mempool()[1]; // item 0 is the commit, item 1 is the reveal. assert_eq!(reveal_tx.txid(), txid); assert_eq!( reveal_tx.output.first().unwrap().script_pubkey, @@ -424,15 +510,19 @@ fn inscribe_to_specific_destination() { #[test] fn inscribe_to_address_on_different_network() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( "wallet inscribe --destination tb1qsgx55dp6gn53tsmyjjv4c2ye403hgxynxs0dnm --file degenerate.png --fee-rate 1" ) .write("degenerate.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error: address tb1qsgx55dp6gn53tsmyjjv4c2ye403hgxynxs0dnm belongs to network testnet which is different from required bitcoin\n") .run_and_extract_stdout(); @@ -440,32 +530,40 @@ fn inscribe_to_address_on_different_network() { #[test] fn inscribe_with_no_limit() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let four_megger = std::iter::repeat(0).take(4_000_000).collect::>(); CommandBuilder::new("wallet inscribe --no-limit degenerate.png --fee-rate 1") .write("degenerate.png", four_megger) - .rpc_server(&rpc_server); + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server); } #[test] fn inscribe_works_with_postage() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --file foo.txt --postage 5btc --fee-rate 10".to_string()) .write("foo.txt", [0; 350]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let inscriptions = CommandBuilder::new("wallet inscriptions".to_string()) .write("foo.txt", [0; 350]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); pretty_assert_eq!(inscriptions[0].postage, 5 * COIN_VALUE); @@ -473,9 +571,12 @@ fn inscribe_works_with_postage() { #[test] fn inscribe_with_non_existent_parent_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let parent_id = "0000000000000000000000000000000000000000000000000000000000000000i0"; @@ -483,7 +584,8 @@ fn inscribe_with_non_existent_parent_inscription() { "wallet inscribe --fee-rate 1.0 --parent {parent_id} --file child.png" )) .write("child.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr(format!("error: parent {parent_id} does not exist\n")) .expected_exit_code(1) .run_and_extract_stdout(); @@ -491,20 +593,24 @@ fn inscribe_with_non_existent_parent_inscription() { #[test] fn inscribe_with_parent_inscription_and_fee_rate() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let parent_output = CommandBuilder::new("wallet inscribe --fee-rate 5.0 --file parent.png") .write("parent.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); let parent_id = parent_output.inscriptions[0].id; - let commit_tx = &rpc_server.mempool()[0]; - let reveal_tx = &rpc_server.mempool()[1]; + let commit_tx = &bitcoin_rpc_server.mempool()[0]; + let reveal_tx = &bitcoin_rpc_server.mempool()[1]; assert_eq!( ord::FeeRate::try_from(5.0) @@ -514,20 +620,21 @@ fn inscribe_with_parent_inscription_and_fee_rate() { parent_output.total_fees ); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let child_output = CommandBuilder::new(format!( "wallet inscribe --fee-rate 7.3 --parent {parent_id} --file child.png" )) .write("child.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert_eq!(rpc_server.descriptors().len(), 4); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 4); assert_eq!(parent_id, child_output.parent.unwrap()); - let commit_tx = &rpc_server.mempool()[0]; - let reveal_tx = &rpc_server.mempool()[1]; + let commit_tx = &bitcoin_rpc_server.mempool()[0]; + let reveal_tx = &bitcoin_rpc_server.mempool()[1]; assert_eq!( ord::FeeRate::try_from(7.3) @@ -537,11 +644,9 @@ fn inscribe_with_parent_inscription_and_fee_rate() { child_output.total_fees ); - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", child_output.parent.unwrap()), format!( ".*
children
.*
.*", @@ -549,7 +654,7 @@ fn inscribe_with_parent_inscription_and_fee_rate() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", child_output.inscriptions[0].id), format!( ".*
parent
.*
.*", @@ -560,24 +665,30 @@ fn inscribe_with_parent_inscription_and_fee_rate() { #[test] fn reinscribe_with_flag() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 0); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 0); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); let inscribe = CommandBuilder::new("wallet inscribe --file tulip.png --fee-rate 5.0 ") .write("tulip.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); - let txid = rpc_server.mine_blocks(1)[0].txdata[2].txid(); + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[2].txid(); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - let request = ord_server.request(format!("/content/{}", inscribe.inscriptions[0].id)); + let request = ord_rpc_server.request(format!("/content/{}", inscribe.inscriptions[0].id)); assert_eq!(request.status(), 200); @@ -585,16 +696,16 @@ fn reinscribe_with_flag() { "wallet inscribe --file orchid.png --fee-rate 1.1 --reinscribe --satpoint {txid}:0:0" )) .write("orchid.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &["--index-sats"]); - let request = ord_server.request(format!("/content/{}", reinscribe.inscriptions[0].id)); + let request = ord_rpc_server.request(format!("/content/{}", reinscribe.inscriptions[0].id)); assert_eq!(request.status(), 200); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/sat/{}", 50 * COIN_VALUE), format!( ".*
inscriptions
.*
.*.*", @@ -605,25 +716,29 @@ fn reinscribe_with_flag() { #[test] fn with_reinscribe_flag_but_not_actually_a_reinscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - assert_eq!(rpc_server.descriptors().len(), 0); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --file tulip.png --fee-rate 5.0 ") .write("tulip.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let coinbase = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( "wallet inscribe --file orchid.png --fee-rate 1.1 --reinscribe --satpoint {coinbase}:0:0" )) .write("orchid.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error: reinscribe flag set but this would not be a reinscription.*") .run_and_extract_stdout(); @@ -631,28 +746,32 @@ fn with_reinscribe_flag_but_not_actually_a_reinscription() { #[test] fn try_reinscribe_without_flag() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let reveal_txid = CommandBuilder::new("wallet inscribe --file tulip.png --fee-rate 5.0 ") .write("tulip.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .reveal; - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new(format!( "wallet inscribe --file orchid.png --fee-rate 1.1 --satpoint {reveal_txid}:0:0" )) .write("orchid.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex(format!( "error: sat at {reveal_txid}:0:0 already inscribed.*" @@ -662,23 +781,27 @@ fn try_reinscribe_without_flag() { #[test] fn no_metadata_appears_on_inscription_page_if_no_metadata_is_passed() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let Inscribe { inscriptions, .. } = CommandBuilder::new("wallet inscribe --fee-rate 1 --file content.png") .write("content.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); - assert!(!ord_server + assert!(!ord_rpc_server .request(format!("/inscription/{inscription}"),) .text() .unwrap() @@ -687,25 +810,29 @@ fn no_metadata_appears_on_inscription_page_if_no_metadata_is_passed() { #[test] fn json_metadata_appears_on_inscription_page() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let Inscribe { inscriptions, .. } = CommandBuilder::new( "wallet inscribe --fee-rate 1 --json-metadata metadata.json --file content.png", ) .write("content.png", [1; 520]) .write("metadata.json", r#"{"foo": "bar", "baz": 1}"#) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), ".*
metadata
.*
foo
bar
baz
1
.*", ); @@ -713,9 +840,13 @@ fn json_metadata_appears_on_inscription_page() { #[test] fn cbor_metadata_appears_on_inscription_page() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let Inscribe { inscriptions, .. } = CommandBuilder::new( "wallet inscribe --fee-rate 1 --cbor-metadata metadata.cbor --file content.png", @@ -727,16 +858,15 @@ fn cbor_metadata_appears_on_inscription_page() { 0xA2, 0x63, b'f', b'o', b'o', 0x63, b'b', b'a', b'r', 0x63, b'b', b'a', b'z', 0x01, ], ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), ".*
metadata
.*
foo
bar
baz
1
.*", ); @@ -744,14 +874,14 @@ fn cbor_metadata_appears_on_inscription_page() { #[test] fn error_message_when_parsing_json_metadata_is_reasonable() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new( "wallet inscribe --fee-rate 1 --json-metadata metadata.json --file content.png", ) .write("content.png", [1; 520]) .write("metadata.json", "{") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .stderr_regex(".*failed to parse JSON metadata.*") .expected_exit_code(1) .run_and_extract_stdout(); @@ -759,14 +889,14 @@ fn error_message_when_parsing_json_metadata_is_reasonable() { #[test] fn error_message_when_parsing_cbor_metadata_is_reasonable() { - let rpc_server = test_bitcoincore_rpc::spawn(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new( "wallet inscribe --fee-rate 1 --cbor-metadata metadata.cbor --file content.png", ) .write("content.png", [1; 520]) .write("metadata.cbor", [0x61]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .stderr_regex(".*failed to parse CBOR metadata.*") .expected_exit_code(1) .run_and_extract_stdout(); @@ -774,17 +904,20 @@ fn error_message_when_parsing_cbor_metadata_is_reasonable() { #[test] fn batch_inscribe_fails_if_batchfile_has_no_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --fee-rate 2.1 --batch batch.yaml") .write("inscription.txt", "Hello World") .write("batch.yaml", "mode: shared-output\ninscriptions: []\n") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stderr_regex(".*batchfile must contain at least one inscription.*") .expected_exit_code(1) .run_and_extract_stdout(); @@ -792,12 +925,14 @@ fn batch_inscribe_fails_if_batchfile_has_no_inscriptions() { #[test] fn batch_inscribe_can_create_one_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - assert_eq!(rpc_server.descriptors().len(), 0); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --fee-rate 2.1 --batch batch.yaml") .write("inscription.txt", "Hello World") @@ -805,16 +940,15 @@ fn batch_inscribe_can_create_one_inscription() { "batch.yaml", "mode: shared-output\ninscriptions:\n- file: inscription.txt\n metadata: 123\n metaprotocol: foo", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); - - assert_eq!(rpc_server.descriptors().len(), 3); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); - let request = ord_server.request(format!("/content/{}", output.inscriptions[0].id)); + let request = ord_rpc_server.request(format!("/content/{}", output.inscriptions[0].id)); assert_eq!(request.status(), 200); assert_eq!( @@ -823,7 +957,7 @@ fn batch_inscribe_can_create_one_inscription() { ); assert_eq!(request.text().unwrap(), "Hello World"); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), r".*
metadata
\s*
\n 123\n
.*
metaprotocol
\s*
foo
.*", ); @@ -831,12 +965,14 @@ fn batch_inscribe_can_create_one_inscription() { #[test] fn batch_inscribe_with_multiple_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - assert_eq!(rpc_server.descriptors().len(), 0); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --batch batch.yaml --fee-rate 55") .write("inscription.txt", "Hello World") @@ -846,15 +982,15 @@ fn batch_inscribe_with_multiple_inscriptions() { "batch.yaml", "mode: shared-output\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); - let request = TestServer::spawn_with_args(&rpc_server, &[]) - .request(format!("/content/{}", output.inscriptions[0].id)); + let request = ord_rpc_server.request(format!("/content/{}", output.inscriptions[0].id)); assert_eq!(request.status(), 200); assert_eq!( request.headers().get("content-type").unwrap(), @@ -862,34 +998,35 @@ fn batch_inscribe_with_multiple_inscriptions() { ); assert_eq!(request.text().unwrap(), "Hello World"); - let request = TestServer::spawn_with_args(&rpc_server, &[]) - .request(format!("/content/{}", output.inscriptions[1].id)); + let request = ord_rpc_server.request(format!("/content/{}", output.inscriptions[1].id)); assert_eq!(request.status(), 200); assert_eq!(request.headers().get("content-type").unwrap(), "image/png"); - let request = TestServer::spawn_with_args(&rpc_server, &[]) - .request(format!("/content/{}", output.inscriptions[2].id)); + let request = ord_rpc_server.request(format!("/content/{}", output.inscriptions[2].id)); assert_eq!(request.status(), 200); assert_eq!(request.headers().get("content-type").unwrap(), "audio/wav"); } #[test] fn batch_inscribe_with_multiple_inscriptions_with_parent() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let parent_output = CommandBuilder::new("wallet inscribe --fee-rate 5.0 --file parent.png") .write("parent.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); let parent_id = parent_output.inscriptions[0].id; @@ -901,37 +1038,37 @@ fn batch_inscribe_with_multiple_inscriptions_with_parent() { "batch.yaml", format!("parent: {parent_id}\nmode: shared-output\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n") ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), r".*
parent
\s*
.*
.*", ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), r".*
parent
\s*
.*
.*", ); - let request = TestServer::spawn_with_args(&rpc_server, &[]) - .request(format!("/content/{}", output.inscriptions[2].id)); + let request = ord_rpc_server.request(format!("/content/{}", output.inscriptions[2].id)); assert_eq!(request.status(), 200); assert_eq!(request.headers().get("content-type").unwrap(), "audio/wav"); } #[test] fn batch_inscribe_respects_dry_run_flag() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --fee-rate 2.1 --batch batch.yaml --dry-run") .write("inscription.txt", "Hello World") @@ -939,25 +1076,29 @@ fn batch_inscribe_respects_dry_run_flag() { "batch.yaml", "mode: shared-output\ninscriptions:\n- file: inscription.txt\n", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert!(rpc_server.mempool().is_empty()); + assert!(bitcoin_rpc_server.mempool().is_empty()); - let request = TestServer::spawn_with_args(&rpc_server, &[]) - .request(format!("/content/{}", output.inscriptions[0].id)); + let request = ord_rpc_server.request(format!("/content/{}", output.inscriptions[0].id)); assert_eq!(request.status(), 404); } #[test] fn batch_in_same_output_but_different_satpoints() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --fee-rate 1 --batch batch.yaml") .write("inscription.txt", "Hello World") @@ -967,7 +1108,8 @@ fn batch_in_same_output_but_different_satpoints() { "batch.yaml", "mode: shared-output\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); let outpoint = output.inscriptions[0].location.outpoint; @@ -981,13 +1123,11 @@ fn batch_in_same_output_but_different_satpoints() { ); } - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); let outpoint = output.inscriptions[0].location.outpoint; - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), format!( r".*
location
.*
{}:0
.*", @@ -995,7 +1135,7 @@ fn batch_in_same_output_but_different_satpoints() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( r".*
location
.*
{}:10000
.*", @@ -1003,7 +1143,7 @@ fn batch_in_same_output_but_different_satpoints() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), format!( r".*
location
.*
{}:20000
.*", @@ -1011,7 +1151,7 @@ fn batch_in_same_output_but_different_satpoints() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/output/{}", output.inscriptions[0].location.outpoint), format!(r".*
.*.*.*.*.*.*", output.inscriptions[0].id, output.inscriptions[1].id, output.inscriptions[2].id), ); @@ -1019,10 +1159,14 @@ fn batch_in_same_output_but_different_satpoints() { #[test] fn batch_in_same_output_with_non_default_postage() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --fee-rate 1 --batch batch.yaml") .write("inscription.txt", "Hello World") @@ -1032,7 +1176,8 @@ fn batch_in_same_output_with_non_default_postage() { "batch.yaml", "mode: shared-output\npostage: 777\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); let outpoint = output.inscriptions[0].location.outpoint; @@ -1046,13 +1191,11 @@ fn batch_in_same_output_with_non_default_postage() { ); } - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); let outpoint = output.inscriptions[0].location.outpoint; - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), format!( r".*
location
.*
{}:0
.*", @@ -1060,7 +1203,7 @@ fn batch_in_same_output_with_non_default_postage() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( r".*
location
.*
{}:777
.*", @@ -1068,7 +1211,7 @@ fn batch_in_same_output_with_non_default_postage() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), format!( r".*
location
.*
{}:1554
.*", @@ -1076,7 +1219,7 @@ fn batch_in_same_output_with_non_default_postage() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/output/{}", output.inscriptions[0].location.outpoint), format!(r".*.*.*.*.*.*.*", output.inscriptions[0].id, output.inscriptions[1].id, output.inscriptions[2].id), ); @@ -1084,21 +1227,24 @@ fn batch_in_same_output_with_non_default_postage() { #[test] fn batch_in_separate_outputs_with_parent() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let parent_output = CommandBuilder::new("wallet inscribe --fee-rate 5.0 --file parent.png") .write("parent.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); let parent_id = parent_output.inscriptions[0].id; @@ -1110,7 +1256,8 @@ fn batch_in_separate_outputs_with_parent() { "batch.yaml", format!("parent: {parent_id}\nmode: separate-outputs\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n") ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); for inscription in &output.inscriptions { @@ -1125,15 +1272,13 @@ fn batch_in_separate_outputs_with_parent() { outpoints.dedup(); assert_eq!(outpoints.len(), output.inscriptions.len()); - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); let output_1 = output.inscriptions[0].location.outpoint; let output_2 = output.inscriptions[1].location.outpoint; let output_3 = output.inscriptions[2].location.outpoint; - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), format!( r".*
parent
\s*
.*{parent_id}.*
.*
output value
.*
10000
.*.*
location
.*
{}:0
.*", @@ -1141,7 +1286,7 @@ fn batch_in_separate_outputs_with_parent() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( r".*
parent
\s*
.*{parent_id}.*
.*
output value
.*
10000
.*.*
location
.*
{}:0
.*", @@ -1149,7 +1294,7 @@ fn batch_in_separate_outputs_with_parent() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), format!( r".*
parent
\s*
.*{parent_id}.*
.*
output value
.*
10000
.*.*
location
.*
{}:0
.*", @@ -1160,21 +1305,24 @@ fn batch_in_separate_outputs_with_parent() { #[test] fn batch_in_separate_outputs_with_parent_and_non_default_postage() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let parent_output = CommandBuilder::new("wallet inscribe --fee-rate 5.0 --file parent.png") .write("parent.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); let parent_id = parent_output.inscriptions[0].id; @@ -1186,7 +1334,8 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { "batch.yaml", format!("parent: {parent_id}\nmode: separate-outputs\npostage: 777\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n") ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); for inscription in &output.inscriptions { @@ -1202,15 +1351,13 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { outpoints.dedup(); assert_eq!(outpoints.len(), output.inscriptions.len()); - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); let output_1 = output.inscriptions[0].location.outpoint; let output_2 = output.inscriptions[1].location.outpoint; let output_3 = output.inscriptions[2].location.outpoint; - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), format!( r".*
parent
\s*
.*{parent_id}.*
.*
output value
.*
777
.*.*
location
.*
{}:0
.*", @@ -1218,7 +1365,7 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( r".*
parent
\s*
.*{parent_id}.*
.*
output value
.*
777
.*.*
location
.*
{}:0
.*", @@ -1226,7 +1373,7 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), format!( r".*
parent
\s*
.*{parent_id}.*
.*
output value
.*
777
.*.*
location
.*
{}:0
.*", @@ -1237,16 +1384,21 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { #[test] fn inscribe_does_not_pick_locked_utxos() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - let coinbase_tx = &rpc_server.mine_blocks(1)[0].txdata[0]; + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let coinbase_tx = &bitcoin_rpc_server.mine_blocks(1)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); - rpc_server.lock(outpoint); + bitcoin_rpc_server.lock(outpoint); CommandBuilder::new("wallet inscribe --file hello.txt --fee-rate 1") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .write("hello.txt", "HELLOWORLD") .expected_exit_code(1) .stderr_regex("error: wallet contains no cardinal utxos\n") @@ -1255,24 +1407,27 @@ fn inscribe_does_not_pick_locked_utxos() { #[test] fn inscribe_can_compress() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let Inscribe { inscriptions, .. } = CommandBuilder::new("wallet inscribe --compress --file foo.txt --fee-rate 1".to_string()) .write("foo.txt", [0; 350_000]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let test_server = TestServer::spawn_with_args(&rpc_server, &[]); - - test_server.sync_server(); + ord_rpc_server.sync_server(); let client = reqwest::blocking::Client::builder() .brotli(false) @@ -1281,7 +1436,7 @@ fn inscribe_can_compress() { let response = client .get( - test_server + ord_rpc_server .url() .join(format!("/content/{inscription}",).as_ref()) .unwrap(), @@ -1302,7 +1457,7 @@ fn inscribe_can_compress() { let response = client .get( - test_server + ord_rpc_server .url() .join(format!("/content/{inscription}",).as_ref()) .unwrap(), @@ -1316,24 +1471,27 @@ fn inscribe_can_compress() { #[test] fn inscriptions_are_not_compressed_if_no_space_is_saved_by_compression() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let Inscribe { inscriptions, .. } = CommandBuilder::new("wallet inscribe --compress --file foo.txt --fee-rate 1".to_string()) .write("foo.txt", "foo") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); - - let test_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); - test_server.sync_server(); + ord_rpc_server.sync_server(); let client = reqwest::blocking::Client::builder() .brotli(false) @@ -1342,7 +1500,7 @@ fn inscriptions_are_not_compressed_if_no_space_is_saved_by_compression() { let response = client .get( - test_server + ord_rpc_server .url() .join(format!("/content/{inscription}",).as_ref()) .unwrap(), @@ -1356,20 +1514,22 @@ fn inscriptions_are_not_compressed_if_no_space_is_saved_by_compression() { #[test] fn batch_inscribe_fails_if_invalid_network_destination_address() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - rpc_server.mine_blocks(1); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("--regtest wallet inscribe --fee-rate 2.1 --batch batch.yaml") .write("inscription.txt", "Hello World") .write("batch.yaml", "mode: separate-outputs\ninscriptions:\n- file: inscription.txt\n destination: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stderr_regex("error: address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 belongs to network bitcoin which is different from required regtest\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -1377,18 +1537,21 @@ fn batch_inscribe_fails_if_invalid_network_destination_address() { #[test] fn batch_inscribe_fails_with_shared_output_and_destination_set() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - assert_eq!(rpc_server.descriptors().len(), 0); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --fee-rate 2.1 --batch batch.yaml") .write("inscription.txt", "Hello World") .write("tulip.png", "") .write("batch.yaml", "mode: shared-output\ninscriptions:\n- file: inscription.txt\n destination: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\n- file: tulip.png") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error: individual inscription destinations cannot be set in shared-output mode\n") .run_and_extract_stdout(); @@ -1396,12 +1559,14 @@ fn batch_inscribe_fails_with_shared_output_and_destination_set() { #[test] fn batch_inscribe_works_with_some_destinations_set_and_others_not() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --batch batch.yaml --fee-rate 55") .write("inscription.txt", "Hello World") @@ -1411,33 +1576,32 @@ fn batch_inscribe_works_with_some_destinations_set_and_others_not() { "batch.yaml", "mode: separate-outputs\ninscriptions:\n- file: inscription.txt\n destination: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\n- file: tulip.png\n- file: meow.wav\n destination: bc1pxwww0ct9ue7e8tdnlmug5m2tamfn7q06sahstg39ys4c9f3340qqxrdu9k\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), ".*
address
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
.*", ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( ".*
address
{}
.*", - rpc_server.change_addresses()[0] + bitcoin_rpc_server.change_addresses()[0] ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), ".*
address
@@ -1447,10 +1611,14 @@ fn batch_inscribe_works_with_some_destinations_set_and_others_not() { #[test] fn batch_same_sat() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscribe --fee-rate 1 --batch batch.yaml") .write("inscription.txt", "Hello World") @@ -1460,7 +1628,8 @@ fn batch_same_sat() { "batch.yaml", "mode: same-sat\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -1472,13 +1641,11 @@ fn batch_same_sat() { output.inscriptions[2].location ); - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); let outpoint = output.inscriptions[0].location.outpoint; - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), format!( r".*
location
.*
{}:0
.*", @@ -1486,7 +1653,7 @@ fn batch_same_sat() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( r".*
location
.*
{}:0
.*", @@ -1494,7 +1661,7 @@ fn batch_same_sat() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), format!( r".*
location
.*
{}:0
.*", @@ -1502,7 +1669,7 @@ fn batch_same_sat() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/output/{}", output.inscriptions[0].location.outpoint), format!(r".*.*.*.*.*.*.*", output.inscriptions[0].id, output.inscriptions[1].id, output.inscriptions[2].id), ); @@ -1510,17 +1677,22 @@ fn batch_same_sat() { #[test] fn batch_same_sat_with_parent() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let parent_output = CommandBuilder::new("wallet inscribe --fee-rate 5.0 --file parent.png") .write("parent.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let parent_id = parent_output.inscriptions[0].id; @@ -1532,7 +1704,8 @@ fn batch_same_sat_with_parent() { "batch.yaml", format!("mode: same-sat\nparent: {parent_id}\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n") ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -1544,13 +1717,11 @@ fn batch_same_sat_with_parent() { output.inscriptions[2].location ); - rpc_server.mine_blocks(1); - - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); + bitcoin_rpc_server.mine_blocks(1); let txid = output.inscriptions[0].location.outpoint.txid; - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", parent_id), format!( r".*
location
.*
{}:0:0
.*", @@ -1558,7 +1729,7 @@ fn batch_same_sat_with_parent() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[0].id), format!( r".*
location
.*
{}:1:0
.*", @@ -1566,7 +1737,7 @@ fn batch_same_sat_with_parent() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[1].id), format!( r".*
location
.*
{}:1:0
.*", @@ -1574,7 +1745,7 @@ fn batch_same_sat_with_parent() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", output.inscriptions[2].id), format!( r".*
location
.*
{}:1:0
.*", @@ -1582,7 +1753,7 @@ fn batch_same_sat_with_parent() { ), ); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/output/{}", output.inscriptions[0].location.outpoint), format!(r".*.*.*.*.*.*.*", output.inscriptions[0].id, output.inscriptions[1].id, output.inscriptions[2].id), ); @@ -1590,45 +1761,63 @@ fn batch_same_sat_with_parent() { #[test] fn inscribe_with_sat_arg() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(2); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(2); let Inscribe { inscriptions, .. } = CommandBuilder::new( "--index-sats wallet inscribe --file foo.txt --sat 5010000000 --fee-rate 1", ) .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &["--index-sats"]).assert_response_regex( + ord_rpc_server.assert_response_regex( "/sat/5010000000", format!(".*.*"), ); - TestServer::spawn_with_args(&rpc_server, &[]) - .assert_response_regex(format!("/content/{inscription}",), "FOO"); + ord_rpc_server.assert_response_regex(format!("/content/{inscription}",), "FOO"); } #[test] fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet inscribe --file foo.txt --sat 5010000000 --fee-rate 1") .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: index must be built with `--index-sats` to use `--sat`\n") .run_and_extract_stdout(); CommandBuilder::new("--index-sats wallet inscribe --sat 5000000000 --file foo.txt --fee-rate 1") .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + )) .expected_exit_code(1) .expected_stderr("error: could not find sat `5000000000` in wallet outputs\n") .run_and_extract_stdout(); @@ -1636,22 +1825,28 @@ fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { #[test] fn batch_inscribe_with_sat_argument_with_parent() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); - assert_eq!(rpc_server.descriptors().len(), 0); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - create_wallet(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); let parent_output = CommandBuilder::new("--index-sats wallet inscribe --fee-rate 5.0 --file parent.png") .write("parent.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - assert_eq!(rpc_server.descriptors().len(), 3); + assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); let parent_id = parent_output.inscriptions[0].id; @@ -1663,12 +1858,13 @@ fn batch_inscribe_with_sat_argument_with_parent() { "batch.yaml", format!("parent: {parent_id}\nmode: same-sat\nsat: 5000111111\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n") ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &["--index-sats"]).assert_response_regex( + ord_rpc_server.assert_response_regex( "/sat/5000111111", format!( ".*.*.*.*", @@ -1679,9 +1875,14 @@ fn batch_inscribe_with_sat_argument_with_parent() { #[test] fn batch_inscribe_with_sat_arg_fails_if_wrong_mode() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --fee-rate 1 --batch batch.yaml") .write("inscription.txt", "Hello World") @@ -1691,7 +1892,8 @@ fn batch_inscribe_with_sat_arg_fails_if_wrong_mode() { "batch.yaml", "mode: shared-output\nsat: 5000111111\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: `sat` can only be set in `same-sat` mode\n") .run_and_extract_stdout(); @@ -1699,9 +1901,17 @@ fn batch_inscribe_with_sat_arg_fails_if_wrong_mode() { #[test] fn batch_inscribe_with_fee_rate() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(2); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(2); let set_fee_rate = 1.0; @@ -1713,13 +1923,14 @@ fn batch_inscribe_with_fee_rate() { "batch.yaml", "mode: same-sat\nsat: 5000111111\ninscriptions:\n- file: inscription.txt\n- file: tulip.png\n- file: meow.wav\n" ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let commit_tx = &rpc_server.mempool()[0]; + let commit_tx = &bitcoin_rpc_server.mempool()[0]; let mut fee = 0; for input in &commit_tx.input { - fee += rpc_server + fee += bitcoin_rpc_server .get_utxo_amount(&input.previous_output) .unwrap() .to_sat(); @@ -1730,7 +1941,7 @@ fn batch_inscribe_with_fee_rate() { let fee_rate = fee as f64 / commit_tx.vsize() as f64; pretty_assert_eq!(fee_rate, set_fee_rate); - let reveal_tx = &rpc_server.mempool()[1]; + let reveal_tx = &bitcoin_rpc_server.mempool()[1]; let mut fee = 0; for input in &reveal_tx.input { fee += &commit_tx.output[input.previous_output.vout as usize].value; @@ -1752,24 +1963,27 @@ fn batch_inscribe_with_fee_rate() { #[test] fn server_can_decompress_brotli() { - let rpc_server = test_bitcoincore_rpc::spawn(); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let Inscribe { inscriptions, .. } = CommandBuilder::new("wallet inscribe --compress --file foo.txt --fee-rate 1".to_string()) .write("foo.txt", [0; 350_000]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output(); let inscription = inscriptions[0].id; - rpc_server.mine_blocks(1); - - let test_server = TestServer::spawn_with_server_args(&rpc_server, &[], &[]); + bitcoin_rpc_server.mine_blocks(1); - test_server.sync_server(); + ord_rpc_server.sync_server(); let client = reqwest::blocking::Client::builder() .brotli(false) @@ -1778,7 +1992,7 @@ fn server_can_decompress_brotli() { let response = client .get( - test_server + ord_rpc_server .url() .join(format!("/content/{inscription}",).as_ref()) .unwrap(), @@ -1788,7 +2002,7 @@ fn server_can_decompress_brotli() { assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE); - let test_server = TestServer::spawn_with_server_args(&rpc_server, &[], &["--decompress"]); + let test_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--decompress"]); test_server.sync_server(); @@ -1813,35 +2027,45 @@ fn server_can_decompress_brotli() { #[test] fn file_inscribe_with_delegate_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let (delegate, _) = inscribe(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (delegate, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let inscribe = CommandBuilder::new(format!( "wallet inscribe --fee-rate 1.0 --delegate {delegate} --file inscription.txt" )) .write("inscription.txt", "INSCRIPTION") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", inscribe.inscriptions[0].id), format!(r#".*
delegate
\s*
{delegate}
.*"#,), ); - TestServer::spawn_with_args(&rpc_server, &[]) - .assert_response(format!("/content/{}", inscribe.inscriptions[0].id), "FOO"); + ord_rpc_server.assert_response(format!("/content/{}", inscribe.inscriptions[0].id), "FOO"); } #[test] fn file_inscribe_with_non_existent_delegate_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let delegate = "0000000000000000000000000000000000000000000000000000000000000000i0"; @@ -1849,7 +2073,8 @@ fn file_inscribe_with_non_existent_delegate_inscription() { "wallet inscribe --fee-rate 1.0 --delegate {delegate} --file child.png" )) .write("child.png", [1; 520]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr(format!("error: delegate {delegate} does not exist\n")) .expected_exit_code(1) .run_and_extract_stdout(); @@ -1857,11 +2082,16 @@ fn file_inscribe_with_non_existent_delegate_inscription() { #[test] fn batch_inscribe_with_delegate_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (delegate, _) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (delegate, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let inscribe = CommandBuilder::new("wallet inscribe --fee-rate 1.0 --batch batch.yaml") .write("inscription.txt", "INSCRIPTION") @@ -1875,25 +2105,30 @@ inscriptions: " ), ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - TestServer::spawn_with_args(&rpc_server, &[]).assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{}", inscribe.inscriptions[0].id), format!(r#".*
delegate
\s*
{delegate}
.*"#,), ); - TestServer::spawn_with_args(&rpc_server, &[]) - .assert_response(format!("/content/{}", inscribe.inscriptions[0].id), "FOO"); + ord_rpc_server.assert_response(format!("/content/{}", inscribe.inscriptions[0].id), "FOO"); } #[test] fn batch_inscribe_with_non_existent_delegate_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let delegate = "0000000000000000000000000000000000000000000000000000000000000000i0"; @@ -1909,7 +2144,8 @@ inscriptions: " ), ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr(format!("error: delegate {delegate} does not exist\n")) .expected_exit_code(1) .run_and_extract_stdout(); diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 4ff430510c..5387a609ac 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -5,14 +5,20 @@ use { #[test] fn inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let (inscription, reveal) = inscribe(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let output = CommandBuilder::new("wallet inscriptions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output.len(), 1); @@ -24,7 +30,8 @@ fn inscriptions() { ); let address = CommandBuilder::new("wallet receive") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .address; @@ -32,16 +39,18 @@ fn inscriptions() { "wallet send --fee-rate 1 {} {inscription}", address.assume_checked() )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(0) .stdout_regex(".*") .run_and_deserialize_output::() .transaction; - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscriptions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output.len(), 1); @@ -51,22 +60,27 @@ fn inscriptions() { #[test] fn inscriptions_includes_locked_utxos() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); - rpc_server.mine_blocks(1); + let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.lock(OutPoint { + bitcoin_rpc_server.mine_blocks(1); + + bitcoin_rpc_server.lock(OutPoint { txid: reveal, vout: 0, }); let output = CommandBuilder::new("wallet inscriptions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output.len(), 1); @@ -76,20 +90,27 @@ fn inscriptions_includes_locked_utxos() { #[test] fn inscriptions_with_postage() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe(&rpc_server); + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); let output = CommandBuilder::new("wallet inscriptions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].postage, 10000); let address = CommandBuilder::new("wallet receive") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::() .address; @@ -97,15 +118,17 @@ fn inscriptions_with_postage() { "wallet send --fee-rate 1 {} {inscription}", address.assume_checked() )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(0) .stdout_regex(".*") .run_and_extract_stdout(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet inscriptions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].postage, 9889); diff --git a/tests/wallet/outputs.rs b/tests/wallet/outputs.rs index 39a648e38e..784906f599 100644 --- a/tests/wallet/outputs.rs +++ b/tests/wallet/outputs.rs @@ -2,15 +2,20 @@ use {super::*, ord::subcommand::wallet::outputs::Output}; #[test] fn outputs() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let coinbase_tx = &rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let coinbase_tx = &bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); let amount = coinbase_tx.output[0].value; let output = CommandBuilder::new("wallet outputs") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].output, outpoint); @@ -19,17 +24,22 @@ fn outputs() { #[test] fn outputs_includes_locked_outputs() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - let coinbase_tx = &rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let coinbase_tx = &bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); let amount = coinbase_tx.output[0].value; - rpc_server.lock(outpoint); + bitcoin_rpc_server.lock(outpoint); let output = CommandBuilder::new("wallet outputs") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].output, outpoint); @@ -38,17 +48,22 @@ fn outputs_includes_locked_outputs() { #[test] fn outputs_includes_unbound_outputs() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let coinbase_tx = &rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; + let coinbase_tx = &bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); let amount = coinbase_tx.output[0].value; - rpc_server.lock(outpoint); + bitcoin_rpc_server.lock(outpoint); let output = CommandBuilder::new("wallet outputs") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].output, outpoint); diff --git a/tests/wallet/receive.rs b/tests/wallet/receive.rs index 45a8c921b7..3b42e2a51c 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -6,7 +6,7 @@ fn receive() { create_wallet(&rpc_server); let output = CommandBuilder::new("wallet receive") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::(); assert!(output.address.is_valid_for_network(Network::Bitcoin)); diff --git a/tests/wallet/restore.rs b/tests/wallet/restore.rs index f13e601a70..15c6ba20c3 100644 --- a/tests/wallet/restore.rs +++ b/tests/wallet/restore.rs @@ -6,7 +6,7 @@ fn restore_generates_same_descriptors() { let rpc_server = test_bitcoincore_rpc::spawn(); let create::Output { mnemonic, .. } = CommandBuilder::new("wallet create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output(); (mnemonic, rpc_server.descriptors()) @@ -15,7 +15,7 @@ fn restore_generates_same_descriptors() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new(["wallet", "restore", &mnemonic.to_string()]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); assert_eq!(rpc_server.descriptors(), descriptors); @@ -29,7 +29,7 @@ fn restore_generates_same_descriptors_with_passphrase() { let create::Output { mnemonic, .. } = CommandBuilder::new(["wallet", "create", "--passphrase", passphrase]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output(); (mnemonic, rpc_server.descriptors()) @@ -44,7 +44,7 @@ fn restore_generates_same_descriptors_with_passphrase() { passphrase, &mnemonic.to_string(), ]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_extract_stdout(); assert_eq!(rpc_server.descriptors(), descriptors); diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index ca425bd665..23211cc163 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -5,11 +5,16 @@ use { #[test] fn requires_sat_index() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet sats") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: sats requires index created with `--index-sats` flag\n") .run_and_extract_stdout(); @@ -17,12 +22,21 @@ fn requires_sat_index() { #[test] fn sats() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - let second_coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let second_coinbase = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); let output = CommandBuilder::new("--index-sats wallet sats") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].sat, 50 * COIN_VALUE); @@ -31,13 +45,22 @@ fn sats() { #[test] fn sats_from_tsv_success() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - let second_coinbase = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let second_coinbase = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); let output = CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") .write("foo.tsv", "nvtcsezkbtg") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::>(); assert_eq!(output[0].sat, "nvtcsezkbtg"); @@ -46,12 +69,20 @@ fn sats_from_tsv_success() { #[test] fn sats_from_tsv_parse_error() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") .write("foo.tsv", "===") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr( "error: failed to parse sat from string \"===\" on line 1: invalid digit found in string\n", @@ -61,10 +92,19 @@ fn sats_from_tsv_parse_error() { #[test] fn sats_from_tsv_file_not_found() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error: I/O error reading `.*`\nbecause: .*\n") .run_and_extract_stdout(); diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 0de16f7e64..47389df399 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -6,30 +6,35 @@ use { #[test] fn inscriptions_can_be_sent() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let (inscription, _) = inscribe(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription}", )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .stdout_regex(r".*") .run_and_deserialize_output::(); - let txid = rpc_server.mempool()[0].txid(); + let txid = bitcoin_rpc_server.mempool()[0].txid(); assert_eq!(txid, output.transaction); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let send_txid = output.transaction; - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), format!( ".*

Inscription 0

.*
.* @@ -49,15 +54,20 @@ fn inscriptions_can_be_sent() { #[test] fn send_unknown_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {txid}i0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr(format!("error: inscription {txid}i0 not found\n")) .expected_exit_code(1) .run_and_extract_stdout(); @@ -65,26 +75,31 @@ fn send_unknown_inscription() { #[test] fn send_inscribed_sat() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); - let (inscription, _) = inscribe(&rpc_server); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qcqgs2pps4u4yedfyl5pysdjjncs8et5utseepv {inscription}", )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let send_txid = output.transaction; - let ord_server = TestServer::spawn_with_args(&rpc_server, &[]); - ord_server.assert_response_regex( + ord_rpc_server.assert_response_regex( format!("/inscription/{inscription}"), format!( ".*

Inscription 0

.*
location
.*
{send_txid}:0:0
.*", @@ -94,30 +109,42 @@ fn send_inscribed_sat() { #[test] fn send_on_mainnnet_works_with_wallet_named_foo() { - let rpc_server = test_bitcoincore_rpc::spawn(); - let txid = rpc_server.mine_blocks(1)[0].txdata[0].txid(); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); CommandBuilder::new("wallet --name foo create") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); CommandBuilder::new(format!( "wallet --name foo send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {txid}:0:0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); } #[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)[0].txdata[0].txid(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::builder().build(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000)[0].txdata[0].txid(); CommandBuilder::new(format!( "wallet send --fee-rate 1 tb1q6en7qjxgw4ev8xwx94pzdry6a6ky7wlfeqzunz {txid}:0:0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr( "error: address tb1q6en7qjxgw4ev8xwx94pzdry6a6ky7wlfeqzunz belongs to network testnet which is different from required bitcoin\n", ) @@ -127,37 +154,49 @@ fn send_addresses_must_be_valid_for_network() { #[test] 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 bitcoin_rpc_server = test_bitcoincore_rpc::builder().build(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0].txid(); let output = CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {txid}:0:0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - assert_eq!(rpc_server.mempool()[0].txid(), output.transaction); + assert_eq!(bitcoin_rpc_server.mempool()[0].txid(), output.transaction); } #[test] fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - let txid = rpc_server.mine_blocks_with_subsidy(1, 10_000)[0].txdata[0].txid(); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10_000)[0].txdata[0].txid(); CommandBuilder::new(format!( "wallet inscribe --satpoint {txid}:0:0 --file degenerate.png --fee-rate 0" )) .write("degenerate.png", [1; 100]) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let txid = rpc_server.mine_blocks_with_subsidy(1, 100)[0].txdata[0].txid(); + let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 100)[0].txdata[0].txid(); CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {txid}:0:0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: wallet does not contain enough cardinal UTXOs, please add additional funds to wallet.\n") .run_and_extract_stdout(); @@ -165,12 +204,16 @@ fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { #[test] fn do_not_send_within_dust_limit_of_an_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe(&rpc_server); + let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = OutPoint { txid: reveal, @@ -180,7 +223,8 @@ fn do_not_send_within_dust_limit_of_an_inscription() { CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {output}:329" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr(format!( "error: cannot send {output}:329 without also sending inscription {inscription} at {output}:0\n" @@ -190,12 +234,16 @@ fn do_not_send_within_dust_limit_of_an_inscription() { #[test] fn can_send_after_dust_limit_from_an_inscription() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe(&rpc_server); + let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = OutPoint { txid: reveal, @@ -205,20 +253,29 @@ fn can_send_after_dust_limit_from_an_inscription() { CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {output}:330" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); } #[test] fn splitting_merged_inscriptions_is_possible() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(3); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-sats"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(3); let inscription = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); // merging 3 inscriptions into one utxo - let reveal_txid = rpc_server.broadcast_tx(TransactionTemplate { + let reveal_txid = bitcoin_rpc_server.broadcast_tx(TransactionTemplate { inputs: &[ (1, 0, 0, inscription.clone()), (2, 0, 0, inscription.clone()), @@ -228,12 +285,9 @@ fn splitting_merged_inscriptions_is_possible() { ..Default::default() }); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); - let server = - TestServer::spawn_with_server_args(&rpc_server, &["--index-sats"], &["--enable-json-api"]); - - let response = server.json_request(format!("/output/{}:0", reveal_txid)); + let response = ord_rpc_server.json_request(format!("/output/{}:0", reveal_txid)); assert_eq!(response.status(), StatusCode::OK); let output_json: OutputJson = serde_json::from_str(&response.text().unwrap()).unwrap(); @@ -275,7 +329,8 @@ fn splitting_merged_inscriptions_is_possible() { "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {}i0", reveal_txid, )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr(format!( "error: cannot send {reveal_txid}:0:0 without also sending inscription {reveal_txid}i2 at {reveal_txid}:0:{}\n", 100 * COIN_VALUE @@ -287,43 +342,51 @@ fn splitting_merged_inscriptions_is_possible() { "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {}i2", reveal_txid, )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); // splitting second to last CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {}i1", reveal_txid, )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); // splitting send first CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {}i0", reveal_txid, )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); } #[test] fn inscriptions_cannot_be_sent_by_satpoint() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - let (_, reveal) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new(format!( "wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {reveal}:0:0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: inscriptions must be sent by inscription ID\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -331,21 +394,26 @@ fn inscriptions_cannot_be_sent_by_satpoint() { #[test] fn send_btc_with_fee_rate() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new( "wallet send --fee-rate 13.3 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let tx = &rpc_server.mempool()[0]; + let tx = &bitcoin_rpc_server.mempool()[0]; let mut fee = 0; for input in &tx.input { - fee += rpc_server + fee += bitcoin_rpc_server .get_utxo_amount(&input.previous_output) .unwrap() .to_sat(); @@ -359,7 +427,7 @@ fn send_btc_with_fee_rate() { assert!(f64::abs(fee_rate - 13.3) < 0.1); assert_eq!( - rpc_server.sent(), + bitcoin_rpc_server.sent(), &[Sent { amount: 1.0, address: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" @@ -373,19 +441,24 @@ fn send_btc_with_fee_rate() { #[test] fn send_btc_locks_inscriptions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - rpc_server.mine_blocks(1); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( - rpc_server.sent(), + bitcoin_rpc_server.sent(), &[Sent { amount: 1.0, address: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" @@ -402,15 +475,20 @@ fn send_btc_locks_inscriptions() { #[test] fn send_btc_fails_if_lock_unspent_fails() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .fail_lock_unspent(true) .build(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_stderr("error: failed to lock UTXOs\n") .expected_exit_code(1) .run_and_extract_stdout(); @@ -418,22 +496,28 @@ fn send_btc_fails_if_lock_unspent_fails() { #[test] fn wallet_send_with_fee_rate() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let tx = &rpc_server.mempool()[0]; + let tx = &bitcoin_rpc_server.mempool()[0]; let mut fee = 0; for input in &tx.input { - fee += rpc_server + fee += bitcoin_rpc_server .get_utxo_amount(&input.previous_output) .unwrap() .to_sat(); @@ -449,16 +533,22 @@ fn wallet_send_with_fee_rate() { #[test] fn user_must_provide_fee_rate_to_send() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - let (inscription, _) = inscribe(&rpc_server); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription}" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(2) .stderr_regex( ".*error: the following required arguments were not provided: @@ -469,22 +559,28 @@ fn user_must_provide_fee_rate_to_send() { #[test] fn wallet_send_with_fee_rate_and_target_postage() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); - rpc_server.mine_blocks(1); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe(&rpc_server); + bitcoin_rpc_server.mine_blocks(1); + + let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0 --postage 77000sat" )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - let tx = &rpc_server.mempool()[0]; + let tx = &bitcoin_rpc_server.mempool()[0]; let mut fee = 0; for input in &tx.input { - fee += rpc_server + fee += bitcoin_rpc_server .get_utxo_amount(&input.previous_output) .unwrap() .to_sat(); @@ -501,16 +597,21 @@ fn wallet_send_with_fee_rate_and_target_postage() { #[test] fn send_btc_does_not_send_locked_utxos() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - let coinbase_tx = &rpc_server.mine_blocks(1)[0].txdata[0]; + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + + let coinbase_tx = &bitcoin_rpc_server.mine_blocks(1)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); - rpc_server.lock(outpoint); + bitcoin_rpc_server.lock(outpoint); CommandBuilder::new("wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error:.*") .run_and_extract_stdout(); @@ -518,19 +619,26 @@ fn send_btc_does_not_send_locked_utxos() { #[test] fn sending_rune_that_has_not_been_etched_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let coinbase_tx = &rpc_server.mine_blocks(1)[0].txdata[0]; + let coinbase_tx = &bitcoin_rpc_server.mine_blocks(1)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); - rpc_server.lock(outpoint); + bitcoin_rpc_server.lock(outpoint); CommandBuilder::new("--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: rune `FOO` has not been etched\n") .run_and_extract_stdout(); @@ -538,19 +646,26 @@ fn sending_rune_that_has_not_been_etched_is_an_error() { #[test] fn sending_rune_with_excessive_precision_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1.1{}", Rune(RUNE) )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: excessive precision\n") .run_and_extract_stdout(); @@ -558,19 +673,26 @@ fn sending_rune_with_excessive_precision_is_an_error() { #[test] fn sending_rune_with_insufficient_balance_is_an_error() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1001{}", Rune(RUNE) )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: insufficient `AAAAAAAAAAAAA` balance, only 1000\u{00A0}ยข in wallet\n") .run_and_extract_stdout(); @@ -578,25 +700,33 @@ fn sending_rune_with_insufficient_balance_is_an_error() { #[test] fn sending_rune_works() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000{}", Rune(RUNE) )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let balances = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -622,24 +752,32 @@ fn sending_rune_works() { #[test] fn sending_spaced_rune_works() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000Aโ€ขAAAAAAAAAAAA", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let balances = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -665,13 +803,19 @@ fn sending_spaced_rune_works() { #[test] fn sending_rune_with_divisibility_works() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let rune = Rune(RUNE); @@ -681,22 +825,25 @@ fn sending_rune_with_divisibility_works() { rune, ) ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 10.1{}", rune )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let balances = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -731,25 +878,33 @@ fn sending_rune_with_divisibility_works() { #[test] fn sending_rune_leaves_unspent_runes_in_wallet() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 750{}", Rune(RUNE) )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let balances = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( @@ -781,13 +936,13 @@ fn sending_rune_leaves_unspent_runes_in_wallet() { } ); - let tx = rpc_server.tx(3, 1); + let tx = bitcoin_rpc_server.tx(3, 1); assert_eq!(tx.txid(), output.transaction); let address = Address::from_script(&tx.output[1].script_pubkey, Network::Regtest).unwrap(); - assert!(rpc_server + assert!(bitcoin_rpc_server .change_addresses() .iter() .any(|change_address| change_address == &address)); @@ -795,34 +950,40 @@ fn sending_rune_leaves_unspent_runes_in_wallet() { #[test] fn sending_rune_creates_transaction_with_expected_runestone() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); - let rune = Rune(RUNE); + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, rune); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 750{}", - rune, + Rune(RUNE), )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let balances = CommandBuilder::new("--regtest --index-runes balances") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); assert_eq!( balances, ord::subcommand::balances::Output { runes: vec![( - rune, + Rune(RUNE), vec![ ( OutPoint { @@ -847,7 +1008,7 @@ fn sending_rune_creates_transaction_with_expected_runestone() { } ); - let tx = rpc_server.tx(3, 1); + let tx = bitcoin_rpc_server.tx(3, 1); assert_eq!(tx.txid(), output.transaction); @@ -872,24 +1033,32 @@ fn sending_rune_creates_transaction_with_expected_runestone() { #[test] fn error_messages_use_spaced_runes() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch(&rpc_server, Rune(RUNE)); + etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); CommandBuilder::new( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1001Aโ€ขAAAAAAAAAAAA", ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: insufficient `Aโ€ขAAAAAAAAAAAA` balance, only 1000\u{00A0}ยข in wallet\n") .run_and_extract_stdout(); CommandBuilder::new("--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1Fโ€ขOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .expected_stderr("error: rune `FOO` has not been etched\n") .run_and_extract_stdout(); @@ -897,26 +1066,34 @@ fn error_messages_use_spaced_runes() { #[test] fn sending_rune_does_not_send_inscription() { - let rpc_server = test_bitcoincore_rpc::builder() + let bitcoin_rpc_server = test_bitcoincore_rpc::builder() .network(Network::Regtest) .build(); - create_wallet(&rpc_server); + let ord_rpc_server = TestServer::spawn_with_server_args( + &bitcoin_rpc_server, + &["--index-runes", "--regtest"], + &["--enable-json-api"], + ); + + create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - rpc_server.mine_blocks_with_subsidy(1, 10000); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); let rune = Rune(RUNE); CommandBuilder::new("--chain regtest --index-runes wallet inscribe --fee-rate 0 --file foo.txt") .write("foo.txt", "FOO") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 10000); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), balance::Output { cardinal: 10000, @@ -933,14 +1110,16 @@ fn sending_rune_does_not_send_inscription() { rune ) ) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(); - rpc_server.mine_blocks_with_subsidy(1, 0); + bitcoin_rpc_server.mine_blocks_with_subsidy(1, 0); assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .run_and_deserialize_output::(), balance::Output { cardinal: 0, @@ -955,7 +1134,8 @@ fn sending_rune_does_not_send_inscription() { "--chain regtest --index-runes wallet send --fee-rate 0 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000{}", rune )) - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) + .ord_rpc_server(&ord_rpc_server) .expected_exit_code(1) .stderr_regex("error:.*") .run_and_extract_stdout(); diff --git a/tests/wallet/transactions.rs b/tests/wallet/transactions.rs index 8f3fd4ffc9..6bf29ada05 100644 --- a/tests/wallet/transactions.rs +++ b/tests/wallet/transactions.rs @@ -8,7 +8,7 @@ fn transactions() { assert!(rpc_server.loaded_wallets().is_empty()); CommandBuilder::new("wallet transactions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_eq!(rpc_server.loaded_wallets().len(), 1); @@ -17,7 +17,7 @@ fn transactions() { rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet transactions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[0].transaction.to_string(), "[[:xdigit:]]{64}"); @@ -30,14 +30,14 @@ fn transactions_with_limit() { create_wallet(&rpc_server); CommandBuilder::new("wallet transactions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .stdout_regex(".*") .run_and_extract_stdout(); rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet transactions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[0].transaction.to_string(), "[[:xdigit:]]{64}"); @@ -46,14 +46,14 @@ fn transactions_with_limit() { rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet transactions") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[1].transaction.to_string(), "[[:xdigit:]]{64}"); assert_eq!(output[1].confirmations, 2); let output = CommandBuilder::new("wallet transactions --limit 1") - .rpc_server(&rpc_server) + .bitcoin_rpc_server(&rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[0].transaction.to_string(), "[[:xdigit:]]{64}"); From 9633b6088b13f4bad640932d288b94acb5ca2470 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Fri, 19 Jan 2024 16:21:40 -0800 Subject: [PATCH 49/74] Rename helper function --- src/lib.rs | 3 ++- src/subcommand/server.rs | 2 +- tests/balances.rs | 4 ++-- tests/decode.rs | 2 +- tests/etch.rs | 2 +- tests/index.rs | 6 +++--- tests/json_api.rs | 14 ++++++------- tests/lib.rs | 39 ++---------------------------------- tests/runes.rs | 6 +++--- tests/server.rs | 22 ++++++++++---------- tests/wallet/balance.rs | 6 +++--- tests/wallet/cardinals.rs | 2 +- tests/wallet/inscribe.rs | 10 ++++----- tests/wallet/inscriptions.rs | 6 +++--- tests/wallet/send.rs | 32 ++++++++++++++--------------- 15 files changed, 61 insertions(+), 95 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d5f4656f92..a39c96166f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,8 @@ #![allow( clippy::too_many_arguments, clippy::type_complexity, - clippy::result_large_err + clippy::result_large_err, + clippy::large_enum_variant )] #![deny( clippy::cast_lossless, diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 2b813afa65..9c9e6df2df 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -187,7 +187,7 @@ impl Server { log::warn!("Updating index: {error}"); } } - thread::sleep(Duration::from_millis(500)); // TODO: What is a good time here? + thread::sleep(Duration::from_millis(500)); // TIDO: What is a good time here? }); INDEXER.lock().unwrap().replace(index_thread); diff --git a/tests/balances.rs b/tests/balances.rs index 2073a4b481..092ecdf25e 100644 --- a/tests/balances.rs +++ b/tests/balances.rs @@ -45,8 +45,8 @@ fn with_runes() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let a = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); - let b = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); + let a = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let b = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); let output = CommandBuilder::new("--regtest --index-runes balances") .bitcoin_rpc_server(&bitcoin_rpc_server) diff --git a/tests/decode.rs b/tests/decode.rs index 936f8f3d46..14e2bca769 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -99,7 +99,7 @@ fn from_core() { bitcoin_rpc_server.mine_blocks(1); - let (_inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new(format!("decode --txid {reveal}")) diff --git a/tests/etch.rs b/tests/etch.rs index 43f9ba7d33..6b01aa5680 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -152,7 +152,7 @@ fn trying_to_etch_an_existing_rune_is_an_error() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); bitcoin_rpc_server.mine_blocks(1); diff --git a/tests/index.rs b/tests/index.rs index 6a63ecc966..c57b2bd210 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -90,10 +90,10 @@ fn export_inscription_number_to_id_tsv() { let temp_dir = TempDir::new().unwrap(); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); diff --git a/tests/json_api.rs b/tests/json_api.rs index 754947971b..243ce0e52c 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -48,7 +48,7 @@ fn get_sat_with_inscription_and_sat_index() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription_id, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription_id, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let response = ord_rpc_server.json_request(format!("/sat/{}", 50 * COIN_VALUE)); @@ -89,7 +89,7 @@ fn get_sat_with_inscription_on_common_sat_and_more_inscriptions() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -148,7 +148,7 @@ fn get_inscription() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription_id, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription_id, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let response = ord_rpc_server.json_request(format!("/inscription/{}", inscription_id)); @@ -490,7 +490,7 @@ fn get_status() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); let response = ord_rpc_server.json_request("/status"); @@ -544,9 +544,9 @@ fn get_runes() { bitcoin_rpc_server.mine_blocks(3); - let a = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); - let b = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); - let c = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 2)); + let a = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let b = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); + let c = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 2)); bitcoin_rpc_server.mine_blocks(1); diff --git a/tests/lib.rs b/tests/lib.rs index 8087dd3bc0..cfdd40810e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -70,7 +70,7 @@ fn create_wallet_new( .run_and_deserialize_output::(); } -fn inscribe_new( +fn inscribe( bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_rpc_server: &TestServer, ) -> (InscriptionId, Txid) { @@ -92,7 +92,7 @@ fn inscribe_new( (output.inscriptions[0].id, output.reveal) } -fn etch_new( +fn etch( bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_rpc_server: &TestServer, rune: Rune, @@ -123,24 +123,6 @@ fn create_wallet(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) { .run_and_deserialize_output::(); } -fn inscribe(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) -> (InscriptionId, Txid) { - bitcoin_rpc_server.mine_blocks(1); - - let output = CommandBuilder::new(format!( - "--chain {} wallet inscribe --fee-rate 1 --file foo.txt", - bitcoin_rpc_server.network() - )) - .write("foo.txt", "FOO") - .bitcoin_rpc_server(bitcoin_rpc_server) - .run_and_deserialize_output::(); - - bitcoin_rpc_server.mine_blocks(1); - - assert_eq!(output.inscriptions.len(), 1); - - (output.inscriptions[0].id, output.reveal) -} - fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { let mut builder = bitcoin::script::Builder::new() .push_opcode(bitcoin::opcodes::OP_FALSE) @@ -159,23 +141,6 @@ fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { bitcoin::Witness::from_slice(&[script.into_bytes(), Vec::new()]) } -fn etch(rpc_server: &test_bitcoincore_rpc::Handle, rune: Rune) -> Etch { - rpc_server.mine_blocks(1); - - let output = CommandBuilder::new( - format!( - "--index-runes --regtest wallet etch --rune {} --divisibility 0 --fee-rate 0 --supply 1000 --symbol ยข", - rune - ) - ) - .bitcoin_rpc_server(rpc_server) - .run_and_deserialize_output(); - - rpc_server.mine_blocks(1); - - output -} - fn runes(rpc_server: &test_bitcoincore_rpc::Handle) -> BTreeMap { CommandBuilder::new("--index-runes --regtest runes") .bitcoin_rpc_server(rpc_server) diff --git a/tests/runes.rs b/tests/runes.rs index 4ad789c2c1..013be0e9d3 100644 --- a/tests/runes.rs +++ b/tests/runes.rs @@ -47,7 +47,7 @@ fn one_rune() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let etch = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let etch = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); assert_eq!( CommandBuilder::new("--index-runes --regtest runes") @@ -98,8 +98,8 @@ fn two_runes() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let a = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); - let b = etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); + let a = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + let b = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); assert_eq!( CommandBuilder::new("--index-runes --regtest runes") diff --git a/tests/server.rs b/tests/server.rs index 224cb90449..15cd17fa70 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -44,7 +44,7 @@ fn inscription_page() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let ethereum_teleburn_address = CommandBuilder::new(format!("teleburn {inscription}")) .bitcoin_rpc_server(&bitcoin_rpc_server) @@ -102,7 +102,7 @@ fn inscription_appears_on_reveal_transaction_page() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -151,7 +151,7 @@ fn inscription_appears_on_output_page() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -168,7 +168,7 @@ fn inscription_page_after_send() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -205,7 +205,7 @@ fn inscription_content() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -288,7 +288,7 @@ fn inscriptions_page() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); ord_rpc_server.assert_response_regex( "/inscriptions", @@ -312,7 +312,7 @@ fn inscriptions_page_is_sorted() { let mut regex = String::new(); for _ in 0..8 { - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); regex.insert_str(0, &format!(".*.*")); } @@ -326,9 +326,9 @@ fn inscriptions_page_has_next_and_previous() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (a, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - let (b, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); - let (c, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (a, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); + let (b, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); + let (c, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); ord_rpc_server.assert_response_regex( format!("/inscription/{b}"), @@ -492,7 +492,7 @@ fn inscription_transactions_are_stored_with_transaction_index() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (_inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let coinbase = bitcoin_rpc_server.tx(1, 0).txid(); diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index aa91fc7536..82458ddf89 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -58,7 +58,7 @@ fn inscribed_utxos_are_deducted_from_cardinal() { } ); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") @@ -103,7 +103,7 @@ fn runic_utxos_are_deducted_from_cardinal() { } ); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") @@ -148,7 +148,7 @@ fn unsynced_wallet_fails_with_unindexed_output() { &["--no-sync", "--enable-json-api"], ); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet balance") .ord_rpc_server(&no_sync_ord_rpc_server) diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index 0ef82a0ebe..a68d4d7da1 100644 --- a/tests/wallet/cardinals.rs +++ b/tests/wallet/cardinals.rs @@ -12,7 +12,7 @@ fn cardinals() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + inscribe(&bitcoin_rpc_server, &ord_rpc_server); let all_outputs = CommandBuilder::new("wallet outputs") .bitcoin_rpc_server(&bitcoin_rpc_server) diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index b748c7c0a5..ec2a20e2f2 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -15,7 +15,7 @@ fn inscribe_creates_inscriptions() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!(bitcoin_rpc_server.descriptors().len(), 3); @@ -215,7 +215,7 @@ fn refuse_to_reinscribe_sats() { bitcoin_rpc_server.mine_blocks(1); - let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 100); @@ -237,7 +237,7 @@ fn refuse_to_inscribe_already_inscribed_utxo() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let output = OutPoint { txid: reveal, @@ -2036,7 +2036,7 @@ fn file_inscribe_with_delegate_inscription() { bitcoin_rpc_server.mine_blocks(1); - let (delegate, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (delegate, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let inscribe = CommandBuilder::new(format!( "wallet inscribe --fee-rate 1.0 --delegate {delegate} --file inscription.txt" @@ -2091,7 +2091,7 @@ fn batch_inscribe_with_delegate_inscription() { bitcoin_rpc_server.mine_blocks(1); - let (delegate, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (delegate, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let inscribe = CommandBuilder::new("wallet inscribe --fee-rate 1.0 --batch batch.yaml") .write("inscription.txt", "INSCRIPTION") diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 5387a609ac..8fcd423970 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -14,7 +14,7 @@ fn inscriptions() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let output = CommandBuilder::new("wallet inscriptions") .bitcoin_rpc_server(&bitcoin_rpc_server) @@ -69,7 +69,7 @@ fn inscriptions_includes_locked_utxos() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -99,7 +99,7 @@ fn inscriptions_with_postage() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let output = CommandBuilder::new("wallet inscriptions") .bitcoin_rpc_server(&bitcoin_rpc_server) diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 47389df399..03c06f7a60 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -15,7 +15,7 @@ fn inscriptions_can_be_sent() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -84,7 +84,7 @@ fn send_inscribed_sat() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -211,7 +211,7 @@ fn do_not_send_within_dust_limit_of_an_inscription() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (inscription, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -241,7 +241,7 @@ fn can_send_after_dust_limit_from_an_inscription() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -378,7 +378,7 @@ fn inscriptions_cannot_be_sent_by_satpoint() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -450,7 +450,7 @@ fn send_btc_locks_inscriptions() { bitcoin_rpc_server.mine_blocks(1); - let (_, reveal) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet send --fee-rate 1 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 1btc") .bitcoin_rpc_server(&bitcoin_rpc_server) @@ -505,7 +505,7 @@ fn wallet_send_with_fee_rate() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0" @@ -542,7 +542,7 @@ fn user_must_provide_fee_rate_to_send() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription}" @@ -568,7 +568,7 @@ fn wallet_send_with_fee_rate_and_target_postage() { bitcoin_rpc_server.mine_blocks(1); - let (inscription, _) = inscribe_new(&bitcoin_rpc_server, &ord_rpc_server); + let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "wallet send bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 {inscription} --fee-rate 2.0 --postage 77000sat" @@ -658,7 +658,7 @@ fn sending_rune_with_excessive_precision_is_an_error() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1.1{}", @@ -685,7 +685,7 @@ fn sending_rune_with_insufficient_balance_is_an_error() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1001{}", @@ -712,7 +712,7 @@ fn sending_rune_works() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000{}", @@ -764,7 +764,7 @@ fn sending_spaced_rune_works() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1000Aโ€ขAAAAAAAAAAAA", @@ -890,7 +890,7 @@ fn sending_rune_leaves_unspent_runes_in_wallet() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 750{}", @@ -962,7 +962,7 @@ fn sending_rune_creates_transaction_with_expected_runestone() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let output = CommandBuilder::new(format!( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 750{}", @@ -1045,7 +1045,7 @@ fn error_messages_use_spaced_runes() { create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); - etch_new(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); + etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); CommandBuilder::new( "--chain regtest --index-runes wallet send --fee-rate 1 bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw 1001Aโ€ขAAAAAAAAAAAA", From 607e53928edde8e5f1489f7493a5639a8805f1c4 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 21 Jan 2024 11:37:59 -0800 Subject: [PATCH 50/74] Maybe faster now --- src/lib.rs | 16 ++++++++-- src/subcommand.rs | 2 +- src/subcommand/preview.rs | 2 +- src/subcommand/server.rs | 17 ++++++---- src/wallet.rs | 2 +- test-bitcoincore-rpc/src/lib.rs | 2 +- tests/lib.rs | 2 +- tests/server.rs | 8 ++--- tests/test_server.rs | 55 +++++++++++++++++++-------------- 9 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a39c96166f..ae937dde12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ macro_rules! tprintln { }; } -mod arguments; +pub mod arguments; mod blocktime; pub mod chain; mod config; @@ -125,7 +125,7 @@ mod deserialize_from_str; mod epoch; mod fee_rate; mod height; -mod index; +pub mod index; mod inscriptions; mod object; mod options; @@ -203,6 +203,16 @@ fn unbound_outpoint() -> OutPoint { } } +pub fn parse_ord_server_args(args: &str) -> (Options, crate::subcommand::server::Server) { + match Arguments::try_parse_from(args.split_whitespace()) { + Ok(arguments) => match arguments.subcommand { + Subcommand::Server(server) => (arguments.options, server), + subcommand => panic!("unexpected subcommand: {subcommand:?}"), + }, + Err(err) => panic!("error parsing arguments: {err}"), + } +} + fn gracefully_shutdown_indexer() { if let Some(indexer) = INDEXER.lock().unwrap().take() { // We explicitly set this to true to notify the thread to not take on new work @@ -229,6 +239,8 @@ pub fn main() { .unwrap() .iter() .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); + + gracefully_shutdown_indexer(); //TODO }) .expect("Error setting handler"); diff --git a/src/subcommand.rs b/src/subcommand.rs index f9d965596d..b4b482e78a 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -77,7 +77,7 @@ impl Subcommand { } } -pub(crate) trait Output: Send { +pub trait Output: Send { fn print_json(&self); } diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 7646a03763..ee6fc3a101 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -89,7 +89,7 @@ impl Preview { panic!("Bitcoin Core RPC did not respond"); } - thread::sleep(Duration::from_millis(50)); + thread::sleep(Duration::from_millis(50)); } Arguments { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 9c9e6df2df..05f4c61cda 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -125,7 +125,7 @@ impl Display for StaticHtml { } #[derive(Debug, Parser)] -pub(crate) struct Server { +pub struct Server { #[arg( long, help = "Listen on
for incoming requests. [default: 0.0.0.0]" @@ -174,7 +174,7 @@ pub(crate) struct Server { } impl Server { - pub(crate) fn run(self, options: Options, index: Arc, handle: Handle) -> SubcommandResult { + pub fn run(self, options: Options, index: Arc, handle: Handle) -> SubcommandResult { Runtime::new()?.block_on(async { let index_clone = index.clone(); @@ -187,7 +187,12 @@ impl Server { log::warn!("Updating index: {error}"); } } - thread::sleep(Duration::from_millis(500)); // TIDO: What is a good time here? + + if integration_test() { + thread::sleep(Duration::from_millis(100)); + } else { + thread::sleep(Duration::from_millis(5000)); + } }); INDEXER.lock().unwrap().replace(index_thread); @@ -1740,7 +1745,7 @@ mod tests { } while index.statistic(crate::index::Statistic::Commits) == 0 { - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } let client = reqwest::blocking::Client::builder() @@ -1753,12 +1758,12 @@ mod tests { Ok(_) => break, Err(err) => { if i == 400 { - panic!("server failed to start: {err}"); + panic!("ord server failed to start: {err}"); } } } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } Self { diff --git a/src/wallet.rs b/src/wallet.rs index 3e0ed07d20..beac9e682b 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -79,7 +79,7 @@ impl Wallet { bail!("wallet failed to synchronize to index"); } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } } diff --git a/test-bitcoincore-rpc/src/lib.rs b/test-bitcoincore-rpc/src/lib.rs index 51eee13bf4..ed6b68fe39 100644 --- a/test-bitcoincore-rpc/src/lib.rs +++ b/test-bitcoincore-rpc/src/lib.rs @@ -100,7 +100,7 @@ impl Builder { Ok(_) => break, Err(err) => { if i == 400 { - panic!("Server failed to start: {err}"); + panic!("mock bitcoind server failed to start: {err}"); } } } diff --git a/tests/lib.rs b/tests/lib.rs index cfdd40810e..dee56a9663 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -31,7 +31,7 @@ use { io::Write, net::TcpListener, path::{Path, PathBuf}, - process::{Child, Command, Stdio}, + process::{Command, Stdio}, str::{self, FromStr}, thread, time::Duration, diff --git a/tests/server.rs b/tests/server.rs index 15cd17fa70..724929c516 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -384,12 +384,12 @@ fn server_runs_with_rpc_user_and_pass_as_env_vars() { Ok(_) => break, Err(err) => { if i == 400 { - panic!("Server failed to start: {err}"); + panic!("ord server failed to start: {err}"); } } } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } rpc_server.mine_blocks(1); @@ -402,10 +402,10 @@ fn server_runs_with_rpc_user_and_pass_as_env_vars() { } if i == 400 { - panic!("server failed to sync"); + panic!("ord server failed to sync"); } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } child.kill().unwrap(); diff --git a/tests/test_server.rs b/tests/test_server.rs index 19f8a0d5cd..f654e36add 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -1,16 +1,18 @@ use { super::*, - crate::command_builder::ToArgs, + axum_server::Handle, bitcoincore_rpc::{Auth, Client, RpcApi}, + ord::{parse_ord_server_args, Index}, reqwest::blocking::Response, }; pub(crate) struct TestServer { - child: Child, + _index: Arc, + ord_rpc_url: String, + ord_server_handle: Handle, port: u16, #[allow(unused)] tempdir: TempDir, - ord_rpc_url: String, } impl TestServer { @@ -30,17 +32,13 @@ impl TestServer { ord_args: &[&str], ord_server_args: &[&str], ) -> Self { + std::env::set_var("ORD_INTEGRATION_TEST", "1"); + let tempdir = TempDir::new().unwrap(); - let cookie_file = match bitcoin_rpc_server.network().as_str() { - "mainnet" => tempdir.path().join(".cookie"), - network => { - fs::create_dir(tempdir.path().join(network)).unwrap(); - tempdir.path().join(format!("{network}/.cookie")) - } - }; + let cookiefile = tempdir.path().join("cookie"); - fs::write(cookie_file.clone(), "foo:bar").unwrap(); + fs::write(&cookiefile, "username:password").unwrap(); let port = TcpListener::bind("127.0.0.1:0") .unwrap() @@ -48,36 +46,44 @@ impl TestServer { .unwrap() .port(); - let child = Command::new(executable_path("ord")).args(format!( - "--rpc-url {} --bitcoin-data-dir {} --data-dir {} {} server {} --http-port {port} --address 127.0.0.1", + let (options, server) = parse_ord_server_args(&format!( + "ord --rpc-url {} --cookie-file {} --bitcoin-data-dir {} --data-dir {} {} server {} --http-port {port} --address 127.0.0.1", bitcoin_rpc_server.url(), + cookiefile.to_str().unwrap(), tempdir.path().display(), tempdir.path().display(), ord_args.join(" "), ord_server_args.join(" "), - ).to_args()) - .env("ORD_INTEGRATION_TEST", "1") - .current_dir(&tempdir) - .spawn().unwrap(); + )); + + let index = Arc::new(Index::open(&options).unwrap()); + let ord_server_handle = Handle::new(); + + { + let index = index.clone(); + let ord_server_handle = ord_server_handle.clone(); + thread::spawn(|| server.run(options, index, ord_server_handle).unwrap()); + } for i in 0.. { match reqwest::blocking::get(format!("http://127.0.0.1:{port}/status")) { Ok(_) => break, Err(err) => { if i == 400 { - panic!("Server failed to start: {err}"); + panic!("ord server failed to start: {err}"); } } } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } Self { - child, - tempdir, - port, + _index: index, ord_rpc_url: bitcoin_rpc_server.url(), + ord_server_handle, + port, + tempdir, } } @@ -139,13 +145,14 @@ impl TestServer { } else if i == 20 { panic!("index failed to synchronize with chain"); } - thread::sleep(Duration::from_millis(25)); + thread::sleep(Duration::from_millis(50)); } } } impl Drop for TestServer { fn drop(&mut self) { - self.child.kill().unwrap() + self.ord_server_handle.shutdown(); + // self.child.kill().unwrap() } } From 5ede49f19052a5005acf06eab6681346a807f14d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 21 Jan 2024 11:44:33 -0800 Subject: [PATCH 51/74] Placate clippy --- src/lib.rs | 2 +- src/subcommand/preview.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ae937dde12..652c4702c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,7 +240,7 @@ pub fn main() { .iter() .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); - gracefully_shutdown_indexer(); //TODO + gracefully_shutdown_indexer(); // TOD }) .expect("Error setting handler"); diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index ee6fc3a101..7646a03763 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -89,7 +89,7 @@ impl Preview { panic!("Bitcoin Core RPC did not respond"); } - thread::sleep(Duration::from_millis(50)); + thread::sleep(Duration::from_millis(50)); } Arguments { From 1be07996a0a7c2916f518ae807c3abf90238dd27 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 21 Jan 2024 17:51:39 -0800 Subject: [PATCH 52/74] Fix preview command --- src/lib.rs | 2 +- src/subcommand/preview.rs | 128 +++++++++++++++++++------------------- src/subcommand/server.rs | 2 +- tests/core.rs | 16 ++--- 4 files changed, 75 insertions(+), 73 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 652c4702c7..5c54d125fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,7 +240,7 @@ pub fn main() { .iter() .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); - gracefully_shutdown_indexer(); // TOD + // gracefully_shutdown_indexer(); // TOD }) .expect("Error setting handler"); diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs index 7646a03763..8b8057e004 100644 --- a/src/subcommand/preview.rs +++ b/src/subcommand/preview.rs @@ -1,6 +1,6 @@ use {super::*, fee_rate::FeeRate, reqwest::Url, std::sync::atomic}; -#[derive(Debug, Parser)] +#[derive(Debug, Parser, Clone)] pub(crate) struct Preview { #[command(flatten)] server: super::server::Server, @@ -38,7 +38,7 @@ impl Preview { pub(crate) fn run(self) -> SubcommandResult { let tmpdir = TempDir::new()?; - let rpc_port = TcpListener::bind("127.0.0.1:0")?.local_addr()?.port(); + let bitcoin_rpc_port = TcpListener::bind("127.0.0.1:0")?.local_addr()?.port(); let bitcoin_data_dir = tmpdir.path().join("bitcoin"); @@ -57,25 +57,16 @@ impl Preview { .arg("-printtoconsole=0") .arg("-regtest") .arg("-txindex") - .arg(format!("-rpcport={rpc_port}")) + .arg(format!("-rpcport={bitcoin_rpc_port}")) .spawn() .context("failed to spawn `bitcoind`")?, ); - let server_url: Url = { - format!( - "http://127.0.0.1:{}", - TcpListener::bind("127.0.0.1:0")?.local_addr()?.port() // very hacky - ) - .parse() - .unwrap() - }; - let options = Options { chain_argument: Chain::Regtest, bitcoin_data_dir: Some(bitcoin_data_dir.clone()), data_dir: tmpdir.path().into(), - rpc_url: Some(format!("http://127.0.0.1:{rpc_port}")), + rpc_url: Some(format!("http://127.0.0.1:{bitcoin_rpc_port}")), index_sats: true, ..Options::default() }; @@ -92,12 +83,23 @@ impl Preview { thread::sleep(Duration::from_millis(50)); } + let mut ord_server_args = self.server.clone(); + + ord_server_args.enable_json_api = true; + + let ord_server_url: Url = format!( + "http://127.0.0.1:{}", + ord_server_args.http_port.unwrap_or(8080) + ) + .parse() + .unwrap(); + Arguments { options: options.clone(), subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { name: "ord".into(), - no_sync: true, - server_url: server_url.clone(), + no_sync: false, + server_url: ord_server_url.clone(), subcommand: crate::subcommand::wallet::Subcommand::Create( crate::subcommand::wallet::create::Create { passphrase: "".into(), @@ -108,61 +110,50 @@ impl Preview { .run() .unwrap(); - let rpc_client = options.bitcoin_rpc_client(None)?; + let bitcoin_rpc_client = options.bitcoin_rpc_client(None)?; - let address = rpc_client + let address = bitcoin_rpc_client .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))? .require_network(Network::Regtest)?; eprintln!("Mining blocksโ€ฆ"); - rpc_client.generate_to_address(101, &address)?; + bitcoin_rpc_client.generate_to_address(101, &address)?; - { - let options = options.clone(); - let rpc_client = options.bitcoin_rpc_client(None)?; + let running = Arc::new(AtomicBool::new(true)); + + let mining_handle = if let Some(blocktime) = self.blocktime { + let bitcoin_rpc_client = options.bitcoin_rpc_client(None)?; let address = address.clone(); + let running = running.clone(); + + eprintln!( + "Mining blocks every {}...", + "second".tally(blocktime.try_into().unwrap()) + ); + + Some(std::thread::spawn(move || { + while running.load(atomic::Ordering::SeqCst) { + bitcoin_rpc_client.generate_to_address(1, &address).unwrap(); + thread::sleep(Duration::from_secs(blocktime)); + } + })) + } else { + None + }; + + let ord_server_handle = { + let options = options.clone(); std::thread::spawn(move || { - if let Some(blocktime) = self.blocktime { - eprintln!( - "Mining blocks every {}...", - "second".tally(blocktime.try_into().unwrap()) - ); - - let running = Arc::new(AtomicBool::new(true)); - - let handle = { - let running = running.clone(); - - std::thread::spawn(move || { - while running.load(atomic::Ordering::SeqCst) { - rpc_client.generate_to_address(1, &address).unwrap(); - thread::sleep(Duration::from_secs(blocktime)); - } - }) - }; - - Arguments { - options, - subcommand: Subcommand::Server(self.server), - } - .run() - .unwrap(); - - running.store(false, atomic::Ordering::SeqCst); - - handle.join().unwrap(); - } else { - Arguments { - options, - subcommand: Subcommand::Server(self.server), - } - .run() - .unwrap(); + Arguments { + options, + subcommand: Subcommand::Server(ord_server_args), } - }); - } + .run() + .unwrap() + }) + }; if let Some(files) = self.files { for file in files { @@ -171,7 +162,7 @@ impl Preview { subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), no_sync: false, - server_url: server_url.clone(), + server_url: ord_server_url.clone(), subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: None, cbor_metadata: None, @@ -196,10 +187,12 @@ impl Preview { } .run()?; - rpc_client.generate_to_address(1, &address)?; + bitcoin_rpc_client.generate_to_address(1, &address)?; } } + println!("hereasldfkjasdflkjasdf"); + if let Some(batches) = self.batches { for batch in batches { Arguments { @@ -207,7 +200,7 @@ impl Preview { subcommand: Subcommand::Wallet(super::wallet::WalletCommand { name: "ord".into(), no_sync: false, - server_url: server_url.clone(), + server_url: ord_server_url.clone(), subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { batch: Some(batch), cbor_metadata: None, @@ -232,9 +225,18 @@ impl Preview { } .run()?; - rpc_client.generate_to_address(1, &address)?; + bitcoin_rpc_client.generate_to_address(1, &address)?; } } + + ord_server_handle.join().unwrap(); + + running.store(false, atomic::Ordering::SeqCst); + + if let Some(mining_handle) = mining_handle { + mining_handle.join().unwrap(); + } + Ok(None) } } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 05f4c61cda..9e8cc372b2 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -124,7 +124,7 @@ impl Display for StaticHtml { } } -#[derive(Debug, Parser)] +#[derive(Debug, Parser, Clone)] pub struct Server { #[arg( long, diff --git a/tests/core.rs b/tests/core.rs index 986602edf8..63232d16ff 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -54,14 +54,6 @@ fn preview() { thread::sleep(Duration::from_millis(500)); } - assert_regex_match!( - reqwest::blocking::get(format!("http://127.0.0.1:{port}/inscriptions")) - .unwrap() - .text() - .unwrap(), - format!(".*( Date: Sun, 21 Jan 2024 18:38:32 -0800 Subject: [PATCH 53/74] Fixed preview test? --- src/lib.rs | 2 +- tests/core.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5c54d125fe..652c4702c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -240,7 +240,7 @@ pub fn main() { .iter() .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); - // gracefully_shutdown_indexer(); // TOD + gracefully_shutdown_indexer(); // TOD }) .expect("Error setting handler"); diff --git a/tests/core.rs b/tests/core.rs index 63232d16ff..dbe0449d5b 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -22,7 +22,7 @@ fn preview() { .port(); let builder = CommandBuilder::new(format!( - "preview --http-port {port} --files alert.html inscription.txt --batches batch_1.yaml batch_2.yaml --blocktime 1" + "preview --address 127.0.0.1 --http-port {port} --files alert.html inscription.txt --batches batch_1.yaml batch_2.yaml --blocktime 1" )) .write("inscription.txt", "Hello World") .write("alert.html", "") @@ -48,7 +48,7 @@ fn preview() { } if attempt == 100 { - panic!("Server did not respond to status check",); + panic!("Preview server did not respond to status check",); } thread::sleep(Duration::from_millis(500)); @@ -80,6 +80,8 @@ fn preview() { thread::sleep(Duration::from_millis(250)); } + thread::sleep(Duration::from_millis(10000)); + assert_regex_match!( reqwest::blocking::get(format!("http://127.0.0.1:{port}/inscriptions")) .unwrap() From 6e29ccb7f342d09502093c6328229da2b54b1b56 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Sun, 21 Jan 2024 21:28:13 -0800 Subject: [PATCH 54/74] quick fix --- src/subcommand/server.rs | 1 + tests/core.rs | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 9e8cc372b2..d4aeb11107 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -182,6 +182,7 @@ impl Server { if SHUTTING_DOWN.load(atomic::Ordering::Relaxed) { break; } + if !self.no_sync { if let Err(error) = index_clone.update() { log::warn!("Updating index: {error}"); diff --git a/tests/core.rs b/tests/core.rs index dbe0449d5b..d2365e8f43 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -21,6 +21,8 @@ fn preview() { .unwrap() .port(); + let ord_server_url: Url = format!("http://127.0.0.1:{port}").parse().unwrap(); + let builder = CommandBuilder::new(format!( "preview --address 127.0.0.1 --http-port {port} --files alert.html inscription.txt --batches batch_1.yaml batch_2.yaml --blocktime 1" )) @@ -40,8 +42,19 @@ fn preview() { let _child = KillOnDrop(builder.command().spawn().unwrap()); + // Leave some time for bitcoind to mine 100 blocks + thread::sleep(Duration::from_millis(15000)); + + assert_regex_match!( + reqwest::blocking::get(format!("{ord_server_url}inscriptions")) + .unwrap() + .text() + .unwrap(), + format!(".*( Date: Sun, 21 Jan 2024 21:35:48 -0800 Subject: [PATCH 55/74] quick fix --- tests/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core.rs b/tests/core.rs index d2365e8f43..745ee2c306 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -43,7 +43,7 @@ fn preview() { let _child = KillOnDrop(builder.command().spawn().unwrap()); // Leave some time for bitcoind to mine 100 blocks - thread::sleep(Duration::from_millis(15000)); + thread::sleep(Duration::from_millis(25000)); assert_regex_match!( reqwest::blocking::get(format!("{ord_server_url}inscriptions")) From 75a119f07475cc9f1e6f2db1195a8c7fd8306fc2 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 22 Jan 2024 10:49:40 -0800 Subject: [PATCH 56/74] quick fix --- tests/core.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/core.rs b/tests/core.rs index 745ee2c306..ef6eae7b6a 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -45,14 +45,6 @@ fn preview() { // Leave some time for bitcoind to mine 100 blocks thread::sleep(Duration::from_millis(25000)); - assert_regex_match!( - reqwest::blocking::get(format!("{ord_server_url}inscriptions")) - .unwrap() - .text() - .unwrap(), - format!(".*( Date: Mon, 22 Jan 2024 13:56:12 -0800 Subject: [PATCH 57/74] Remove preview command --- docs/src/guides/explorer.md | 4 - justfile | 3 - src/index/testing.rs | 2 +- src/lib.rs | 6 +- src/options.rs | 2 +- src/subcommand.rs | 4 - src/subcommand/preview.rs | 242 ------------------------------ src/subcommand/server.rs | 1 + src/subcommand/wallet/inscribe.rs | 1 + tests/core.rs | 95 ------------ tests/lib.rs | 1 - 11 files changed, 6 insertions(+), 355 deletions(-) delete mode 100644 src/subcommand/preview.rs delete mode 100644 tests/core.rs diff --git a/docs/src/guides/explorer.md b/docs/src/guides/explorer.md index 1f120646e4..ad5cc9cbcb 100644 --- a/docs/src/guides/explorer.md +++ b/docs/src/guides/explorer.md @@ -19,10 +19,6 @@ To enable the JSON-API endpoints add the `--enable-json-api` or `-j` flag (see `ord server --enable-json-api` -To test how your inscriptions will look you can run: - -`ord preview ...` - Search ------ diff --git a/justfile b/justfile index f14591983e..216b2fae37 100644 --- a/justfile +++ b/justfile @@ -171,9 +171,6 @@ update-changelog: echo >> CHANGELOG.md git log --pretty='format:- %s' >> CHANGELOG.md -preview-examples: - cargo run preview examples/* - convert-logo-to-favicon: convert -background none -resize 256x256 logo.svg static/favicon.png diff --git a/src/index/testing.rs b/src/index/testing.rs index 04f21a594b..28df035d89 100644 --- a/src/index/testing.rs +++ b/src/index/testing.rs @@ -1,4 +1,4 @@ -use super::*; +use {super::*, std::ffi::OsString, tempfile::TempDir}; pub(crate) struct ContextBuilder { args: Vec, diff --git a/src/lib.rs b/src/lib.rs index 652c4702c7..1b2dd9abf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,15 +60,14 @@ use { cmp, collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}, env, - ffi::OsString, fmt::{self, Display, Formatter}, fs::{self, File}, io::{self, Cursor}, mem, - net::{TcpListener, ToSocketAddrs}, + net::ToSocketAddrs, ops::{Add, AddAssign, Sub}, path::{Path, PathBuf}, - process::{self, Command}, + process, str::FromStr, sync::{ atomic::{self, AtomicBool}, @@ -78,7 +77,6 @@ use { time::{Duration, Instant, SystemTime}, }, sysinfo::System, - tempfile::TempDir, templates::{InscriptionJson, OutputJson, RuneJson, StatusJson}, tokio::{runtime::Runtime, task}, }; diff --git a/src/options.rs b/src/options.rs index 80aae2527d..5668ecb54d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -253,7 +253,7 @@ impl Options { #[cfg(test)] mod tests { - use {super::*, bitcoin::Network, std::path::Path}; + use {super::*, bitcoin::Network, std::path::Path, tempfile::TempDir}; #[test] fn rpc_url_overrides_network() { diff --git a/src/subcommand.rs b/src/subcommand.rs index b4b482e78a..3f0e57e75b 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -7,7 +7,6 @@ pub mod find; pub mod index; pub mod list; pub mod parse; -mod preview; pub mod runes; pub(crate) mod server; pub mod subsidy; @@ -32,8 +31,6 @@ pub(crate) enum Subcommand { List(list::List), #[command(about = "Parse a satoshi from ordinal notation")] Parse(parse::Parse), - #[command(about = "Run an explorer server populated with inscriptions")] - Preview(preview::Preview), #[command(about = "List all runes")] Runes, #[command(about = "Run the explorer server")] @@ -60,7 +57,6 @@ impl Subcommand { Self::Index(index) => index.run(options), Self::List(list) => list.run(options), Self::Parse(parse) => parse.run(), - Self::Preview(preview) => preview.run(), Self::Runes => runes::run(options), Self::Server(server) => { let index = Arc::new(Index::open(&options)?); diff --git a/src/subcommand/preview.rs b/src/subcommand/preview.rs deleted file mode 100644 index 8b8057e004..0000000000 --- a/src/subcommand/preview.rs +++ /dev/null @@ -1,242 +0,0 @@ -use {super::*, fee_rate::FeeRate, reqwest::Url, std::sync::atomic}; - -#[derive(Debug, Parser, Clone)] -pub(crate) struct Preview { - #[command(flatten)] - server: super::server::Server, - #[arg( - num_args = 0.., - long, - help = "Inscribe inscriptions defined in ." - )] - batches: Option>, - #[arg(long, help = "Automatically mine a block every seconds.")] - blocktime: Option, - #[arg(num_args = 0.., long, help = "Inscribe contents of .")] - files: Option>, -} - -#[derive(Debug, Parser)] -pub(crate) struct Batch { - batch_files: Vec, -} - -#[derive(Debug, Parser)] -pub(crate) struct File { - files: Vec, -} - -struct KillOnDrop(process::Child); - -impl Drop for KillOnDrop { - fn drop(&mut self) { - self.0.kill().unwrap() - } -} - -impl Preview { - pub(crate) fn run(self) -> SubcommandResult { - let tmpdir = TempDir::new()?; - - let bitcoin_rpc_port = TcpListener::bind("127.0.0.1:0")?.local_addr()?.port(); - - let bitcoin_data_dir = tmpdir.path().join("bitcoin"); - - fs::create_dir(&bitcoin_data_dir)?; - - eprintln!("Spawning bitcoindโ€ฆ"); - - let _bitcoind = KillOnDrop( - Command::new("bitcoind") - .arg({ - let mut arg = OsString::from("-datadir="); - arg.push(&bitcoin_data_dir); - arg - }) - .arg("-listen=0") - .arg("-printtoconsole=0") - .arg("-regtest") - .arg("-txindex") - .arg(format!("-rpcport={bitcoin_rpc_port}")) - .spawn() - .context("failed to spawn `bitcoind`")?, - ); - - let options = Options { - chain_argument: Chain::Regtest, - bitcoin_data_dir: Some(bitcoin_data_dir.clone()), - data_dir: tmpdir.path().into(), - rpc_url: Some(format!("http://127.0.0.1:{bitcoin_rpc_port}")), - index_sats: true, - ..Options::default() - }; - - for attempt in 0.. { - if options.bitcoin_rpc_client(None).is_ok() { - break; - } - - if attempt == 100 { - panic!("Bitcoin Core RPC did not respond"); - } - - thread::sleep(Duration::from_millis(50)); - } - - let mut ord_server_args = self.server.clone(); - - ord_server_args.enable_json_api = true; - - let ord_server_url: Url = format!( - "http://127.0.0.1:{}", - ord_server_args.http_port.unwrap_or(8080) - ) - .parse() - .unwrap(); - - Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(crate::subcommand::wallet::WalletCommand { - name: "ord".into(), - no_sync: false, - server_url: ord_server_url.clone(), - subcommand: crate::subcommand::wallet::Subcommand::Create( - crate::subcommand::wallet::create::Create { - passphrase: "".into(), - }, - ), - }), - } - .run() - .unwrap(); - - let bitcoin_rpc_client = options.bitcoin_rpc_client(None)?; - - let address = bitcoin_rpc_client - .get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32m))? - .require_network(Network::Regtest)?; - - eprintln!("Mining blocksโ€ฆ"); - - bitcoin_rpc_client.generate_to_address(101, &address)?; - - let running = Arc::new(AtomicBool::new(true)); - - let mining_handle = if let Some(blocktime) = self.blocktime { - let bitcoin_rpc_client = options.bitcoin_rpc_client(None)?; - let address = address.clone(); - let running = running.clone(); - - eprintln!( - "Mining blocks every {}...", - "second".tally(blocktime.try_into().unwrap()) - ); - - Some(std::thread::spawn(move || { - while running.load(atomic::Ordering::SeqCst) { - bitcoin_rpc_client.generate_to_address(1, &address).unwrap(); - thread::sleep(Duration::from_secs(blocktime)); - } - })) - } else { - None - }; - - let ord_server_handle = { - let options = options.clone(); - - std::thread::spawn(move || { - Arguments { - options, - subcommand: Subcommand::Server(ord_server_args), - } - .run() - .unwrap() - }) - }; - - if let Some(files) = self.files { - for file in files { - Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(super::wallet::WalletCommand { - name: "ord".into(), - no_sync: false, - server_url: ord_server_url.clone(), - subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { - batch: None, - cbor_metadata: None, - commit_fee_rate: None, - compress: false, - delegate: None, - destination: None, - dry_run: false, - fee_rate: FeeRate::try_from(1.0).unwrap(), - file: Some(file), - json_metadata: None, - metaprotocol: None, - no_backup: true, - no_limit: false, - parent: None, - postage: Some(TARGET_POSTAGE), - reinscribe: false, - sat: None, - satpoint: None, - }), - }), - } - .run()?; - - bitcoin_rpc_client.generate_to_address(1, &address)?; - } - } - - println!("hereasldfkjasdflkjasdf"); - - if let Some(batches) = self.batches { - for batch in batches { - Arguments { - options: options.clone(), - subcommand: Subcommand::Wallet(super::wallet::WalletCommand { - name: "ord".into(), - no_sync: false, - server_url: ord_server_url.clone(), - subcommand: super::wallet::Subcommand::Inscribe(super::wallet::inscribe::Inscribe { - batch: Some(batch), - cbor_metadata: None, - commit_fee_rate: None, - compress: false, - delegate: None, - destination: None, - dry_run: false, - fee_rate: FeeRate::try_from(1.0).unwrap(), - file: None, - json_metadata: None, - metaprotocol: None, - no_backup: true, - no_limit: false, - parent: None, - postage: Some(TARGET_POSTAGE), - reinscribe: false, - sat: None, - satpoint: None, - }), - }), - } - .run()?; - - bitcoin_rpc_client.generate_to_address(1, &address)?; - } - } - - ord_server_handle.join().unwrap(); - - running.store(false, atomic::Ordering::SeqCst); - - if let Some(mining_handle) = mining_handle { - mining_handle.join().unwrap(); - } - - Ok(None) - } -} diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index d4aeb11107..4655416e05 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1620,6 +1620,7 @@ mod tests { reqwest::Url, serde::de::DeserializeOwned, std::net::TcpListener, + tempfile::TempDir, }; const RUNE: u128 = 99246114928149462; diff --git a/src/subcommand/wallet/inscribe.rs b/src/subcommand/wallet/inscribe.rs index e74aa234f1..e22a0a682c 100644 --- a/src/subcommand/wallet/inscribe.rs +++ b/src/subcommand/wallet/inscribe.rs @@ -201,6 +201,7 @@ mod tests { crate::wallet::inscribe::{BatchEntry, ParentInfo}, bitcoin::policy::MAX_STANDARD_TX_WEIGHT, serde_yaml::{Mapping, Value}, + tempfile::TempDir, }; #[test] diff --git a/tests/core.rs b/tests/core.rs deleted file mode 100644 index ef6eae7b6a..0000000000 --- a/tests/core.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::*; - -struct KillOnDrop(std::process::Child); - -impl Drop for KillOnDrop { - fn drop(&mut self) { - assert!(Command::new("kill") - .arg(self.0.id().to_string()) - .status() - .unwrap() - .success()); - } -} - -#[test] -#[ignore] -fn preview() { - let port = TcpListener::bind("127.0.0.1:0") - .unwrap() - .local_addr() - .unwrap() - .port(); - - let ord_server_url: Url = format!("http://127.0.0.1:{port}").parse().unwrap(); - - let builder = CommandBuilder::new(format!( - "preview --address 127.0.0.1 --http-port {port} --files alert.html inscription.txt --batches batch_1.yaml batch_2.yaml --blocktime 1" - )) - .write("inscription.txt", "Hello World") - .write("alert.html", "") - .write("poem.txt", "Sphinx of black quartz, judge my vow.") - .write("tulip.png", [0; 555]) - .write("meow.wav", [0; 2048]) - .write( - "batch_1.yaml", - "mode: shared-output\ninscriptions:\n- file: poem.txt\n- file: tulip.png\n", - ) - .write( - "batch_2.yaml", - "mode: shared-output\ninscriptions:\n- file: meow.wav\n", - ); - - let _child = KillOnDrop(builder.command().spawn().unwrap()); - - // Leave some time for bitcoind to mine 100 blocks - thread::sleep(Duration::from_millis(25000)); - - for attempt in 0.. { - if let Ok(response) = reqwest::blocking::get(format!("{ord_server_url}status")) { - if response.status() == 200 { - break; - } - } - - if attempt == 100 { - panic!("Preview server did not respond to status check",); - } - - thread::sleep(Duration::from_millis(500)); - } - - let blockheight = reqwest::blocking::get(format!("{ord_server_url}blockheight")) - .unwrap() - .text() - .unwrap() - .parse::() - .unwrap(); - - for attempt in 0.. { - if attempt == 20 { - panic!("Bitcoin Core did not mine blocks",); - } - - if reqwest::blocking::get(format!("{ord_server_url}blockheight")) - .unwrap() - .text() - .unwrap() - .parse::() - .unwrap() - > blockheight - { - break; - } - - thread::sleep(Duration::from_millis(250)); - } - - assert_regex_match!( - reqwest::blocking::get(format!("{ord_server_url}inscriptions")) - .unwrap() - .text() - .unwrap(), - format!(".*( Date: Mon, 22 Jan 2024 15:33:52 -0800 Subject: [PATCH 58/74] remove unused options field --- src/index/testing.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index/testing.rs b/src/index/testing.rs index 28df035d89..bb705861a5 100644 --- a/src/index/testing.rs +++ b/src/index/testing.rs @@ -36,7 +36,6 @@ impl ContextBuilder { index.update().unwrap(); Ok(Context { - _options: options, rpc_server, tempdir, index, @@ -65,7 +64,6 @@ impl ContextBuilder { } pub(crate) struct Context { - pub(crate) _options: Options, pub(crate) rpc_server: test_bitcoincore_rpc::Handle, #[allow(unused)] pub(crate) tempdir: TempDir, From e375a7aa46019a923ca81722c52ed6e4cb2f7eb7 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:34:32 -0800 Subject: [PATCH 59/74] Sort --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1b2dd9abf3..af64d7a6bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ #![allow( - clippy::too_many_arguments, - clippy::type_complexity, + clippy::large_enum_variant, clippy::result_large_err, - clippy::large_enum_variant + clippy::too_many_arguments, + clippy::type_complexity )] #![deny( clippy::cast_lossless, From b986bbe97dac5364a9801d9f415195579e782112 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:37:13 -0800 Subject: [PATCH 60/74] Gracefully shut down index in ctrl-c handler --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index af64d7a6bc..f7b6cb6de9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -238,7 +238,7 @@ pub fn main() { .iter() .for_each(|handle| handle.graceful_shutdown(Some(Duration::from_millis(100)))); - gracefully_shutdown_indexer(); // TOD + gracefully_shutdown_indexer(); }) .expect("Error setting handler"); From 5b6b5bdb7ac5a0cbdb31402e155d6b964e1ec1f7 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:37:54 -0800 Subject: [PATCH 61/74] Move shutdown into success branch --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7b6cb6de9..2ee4bc0706 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,8 +264,7 @@ pub fn main() { if let Some(output) = output { output.print_json(); } + gracefully_shutdown_indexer(); } } - - gracefully_shutdown_indexer(); } From 151cd4d56f591a6f62c274937f2b6da85d934725 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:43:18 -0800 Subject: [PATCH 62/74] Tweak --- src/wallet.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index beac9e682b..4f93dcba63 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -354,15 +354,13 @@ impl Wallet { } pub(crate) fn get_server_status(&self) -> Result { - let status: StatusJson = serde_json::from_str( + Ok(serde_json::from_str( &self .ord_client()? .get(self.ord_url.join("/status").unwrap()) .send()? .text()?, - )?; - - Ok(status) + )?) } pub(crate) fn check_rune_index(&self) -> Result { From 050a3cafb93ab678b2c91c6a31b13d85168e825b Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:44:18 -0800 Subject: [PATCH 63/74] check -> has --- src/subcommand/wallet/balance.rs | 4 ++-- src/subcommand/wallet/etch.rs | 2 +- src/subcommand/wallet/sats.rs | 2 +- src/subcommand/wallet/send.rs | 2 +- src/wallet.rs | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/subcommand/wallet/balance.rs b/src/subcommand/wallet/balance.rs index 5d68d8a0b3..9488644793 100644 --- a/src/subcommand/wallet/balance.rs +++ b/src/subcommand/wallet/balance.rs @@ -43,8 +43,8 @@ pub(crate) fn run(wallet: Wallet) -> SubcommandResult { Ok(Some(Box::new(Output { cardinal, ordinal, - runes: wallet.check_rune_index()?.then_some(runes), - runic: wallet.check_rune_index()?.then_some(runic), + runes: wallet.has_rune_index()?.then_some(runes), + runic: wallet.has_rune_index()?.then_some(runic), total: cardinal + ordinal + runic, }))) } diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index e290426ca6..6237e2db7d 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -23,7 +23,7 @@ pub struct Output { impl Etch { pub(crate) fn run(self, wallet: Wallet) -> SubcommandResult { ensure!( - wallet.check_rune_index()?, + wallet.has_rune_index()?, "`ord wallet etch` requires index created with `--index-runes` flag", ); diff --git a/src/subcommand/wallet/sats.rs b/src/subcommand/wallet/sats.rs index 35d3ea5584..e279a45b9e 100644 --- a/src/subcommand/wallet/sats.rs +++ b/src/subcommand/wallet/sats.rs @@ -26,7 +26,7 @@ pub struct OutputRare { impl Sats { pub(crate) fn run(&self, wallet: Wallet) -> SubcommandResult { ensure!( - wallet.check_sat_index()?, + wallet.has_sat_index()?, "sats requires index created with `--index-sats` flag" ); diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 15955b516e..0a275820b9 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -159,7 +159,7 @@ impl Send { wallet: &Wallet, ) -> Result { ensure!( - wallet.check_rune_index()?, + wallet.has_rune_index()?, "sending runes with `ord send` requires index created with `--index-runes` flag", ); diff --git a/src/wallet.rs b/src/wallet.rs index 4f93dcba63..a333757717 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -140,7 +140,7 @@ impl Wallet { pub(crate) fn get_output_sat_ranges(&self) -> Result)>> { ensure!( - self.check_sat_index()?, + self.has_sat_index()?, "index must be built with `--index-sats` to use `--sat`" ); @@ -162,7 +162,7 @@ impl Wallet { utxos: &BTreeMap, ) -> Result { ensure!( - self.check_sat_index()?, + self.has_sat_index()?, "index must be built with `--index-sats` to use `--sat`" ); @@ -363,11 +363,11 @@ impl Wallet { )?) } - pub(crate) fn check_rune_index(&self) -> Result { + pub(crate) fn has_rune_index(&self) -> Result { Ok(self.get_server_status()?.rune_index) } - pub(crate) fn check_sat_index(&self) -> Result { + pub(crate) fn has_sat_index(&self) -> Result { Ok(self.get_server_status()?.sat_index) } From a1dc47d070881896bcc42b0a3b8c270d7d445203 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:45:53 -0800 Subject: [PATCH 64/74] Avoid clone --- src/subcommand/wallet/create.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs index 3e77becce8..4a99c4d991 100644 --- a/src/subcommand/wallet/create.rs +++ b/src/subcommand/wallet/create.rs @@ -26,7 +26,7 @@ impl Create { let mnemonic = Mnemonic::from_entropy(&entropy)?; - wallet.initialize(mnemonic.to_seed(self.passphrase.clone()))?; + wallet.initialize(mnemonic.to_seed(&self.passphrase))?; Ok(Some(Box::new(Output { mnemonic, From 7755af301a0e1167562a14890b5440a44a96b09d Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:47:37 -0800 Subject: [PATCH 65/74] Reuse bitcoin client --- src/subcommand/wallet/etch.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/subcommand/wallet/etch.rs b/src/subcommand/wallet/etch.rs index 6237e2db7d..8e5d38d5e1 100644 --- a/src/subcommand/wallet/etch.rs +++ b/src/subcommand/wallet/etch.rs @@ -29,7 +29,9 @@ impl Etch { let SpacedRune { rune, spacers } = self.rune; - let count = wallet.bitcoin_client()?.get_block_count()?; + let bitcoin_client = wallet.bitcoin_client()?; + + let count = bitcoin_client.get_block_count()?; ensure!( wallet.get_rune(rune)?.is_none(), @@ -104,24 +106,18 @@ impl Etch { .map(|satpoint| satpoint.outpoint) .collect::>(); - if !wallet.bitcoin_client()?.lock_unspent(&inscriptions)? { + if !bitcoin_client.lock_unspent(&inscriptions)? { bail!("failed to lock UTXOs"); } - let unsigned_transaction = fund_raw_transaction( - &wallet.bitcoin_client()?, - self.fee_rate, - &unfunded_transaction, - )?; + let unsigned_transaction = + fund_raw_transaction(&bitcoin_client, self.fee_rate, &unfunded_transaction)?; - let signed_transaction = wallet - .bitcoin_client()? + let signed_transaction = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let transaction = wallet - .bitcoin_client()? - .send_raw_transaction(&signed_transaction)?; + let transaction = bitcoin_client.send_raw_transaction(&signed_transaction)?; Ok(Some(Box::new(Output { rune: self.rune, From fe7197b99ba124d260bc90869c26a6840b8bb482 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:52:27 -0800 Subject: [PATCH 66/74] Reuse client --- src/subcommand/wallet/send.rs | 38 +++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 0a275820b9..7fb5bfb31b 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -33,9 +33,16 @@ impl Send { let runic_outputs = wallet.get_runic_outputs()?; + let bitcoin_client = wallet.bitcoin_client()?; + let satpoint = match self.outgoing { Outgoing::Amount(amount) => { - Self::lock_non_cardinal_outputs(&wallet, &inscriptions, &runic_outputs, unspent_outputs)?; + Self::lock_non_cardinal_outputs( + &bitcoin_client, + &inscriptions, + &runic_outputs, + unspent_outputs, + )?; let transaction = Self::send_amount(&wallet, amount, address, self.fee_rate)?; return Ok(Some(Box::new(Output { transaction }))); } @@ -43,6 +50,7 @@ impl Send { Outgoing::Rune { decimal, rune } => { let transaction = Self::send_runes( address, + &bitcoin_client, decimal, self.fee_rate, inscriptions, @@ -90,18 +98,17 @@ impl Send { ) .build_transaction()?; - let signed_tx = wallet - .bitcoin_client()? + let signed_tx = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - let txid = wallet.bitcoin_client()?.send_raw_transaction(&signed_tx)?; + let txid = bitcoin_client.send_raw_transaction(&signed_tx)?; Ok(Some(Box::new(Output { transaction: txid }))) } fn lock_non_cardinal_outputs( - wallet: &Wallet, + bitcoin_client: &Client, inscriptions: &BTreeMap, runic_outputs: &BTreeSet, unspent_outputs: BTreeMap, @@ -118,7 +125,7 @@ impl Send { .cloned() .collect::>(); - if !wallet.bitcoin_client()?.lock_unspent(&locked_outputs)? { + if !bitcoin_client.lock_unspent(&locked_outputs)? { bail!("failed to lock UTXOs"); } @@ -150,6 +157,7 @@ impl Send { fn send_runes( address: Address, + bitcoin_client: &Client, decimal: Decimal, fee_rate: FeeRate, inscriptions: BTreeMap, @@ -163,7 +171,12 @@ impl Send { "sending runes with `ord send` requires index created with `--index-runes` flag", ); - Self::lock_non_cardinal_outputs(wallet, &inscriptions, &runic_outputs, unspent_outputs)?; + Self::lock_non_cardinal_outputs( + bitcoin_client, + &inscriptions, + &runic_outputs, + unspent_outputs, + )?; let (id, entry, _parent) = wallet .get_rune(spaced_rune.rune)? @@ -245,17 +258,12 @@ impl Send { }; let unsigned_transaction = - fund_raw_transaction(&wallet.bitcoin_client()?, fee_rate, &unfunded_transaction)?; + fund_raw_transaction(&bitcoin_client, fee_rate, &unfunded_transaction)?; - let signed_transaction = wallet - .bitcoin_client()? + let signed_transaction = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? .hex; - Ok( - wallet - .bitcoin_client()? - .send_raw_transaction(&signed_transaction)?, - ) + Ok(bitcoin_client.send_raw_transaction(&signed_transaction)?) } } From 68aadc880060ced1ee53308107e163d206cdfd48 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:55:05 -0800 Subject: [PATCH 67/74] Rename modules --- src/wallet/inscribe.rs | 6 +++--- src/wallet/inscribe/{batchentry.rs => batch_entry.rs} | 0 src/wallet/inscribe/{batchfile.rs => batch_file.rs} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename src/wallet/inscribe/{batchentry.rs => batch_entry.rs} (100%) rename src/wallet/inscribe/{batchfile.rs => batch_file.rs} (100%) diff --git a/src/wallet/inscribe.rs b/src/wallet/inscribe.rs index 427a89edef..87783ae256 100644 --- a/src/wallet/inscribe.rs +++ b/src/wallet/inscribe.rs @@ -14,11 +14,11 @@ use { wallet::transaction_builder::Target, }; -pub use {batch::Batch, batchentry::BatchEntry, batchfile::Batchfile, mode::Mode}; +pub use {batch::Batch, batch_entry::BatchEntry, batch_file::Batchfile, mode::Mode}; pub mod batch; -pub mod batchentry; -pub mod batchfile; +pub mod batch_entry; +pub mod batch_file; pub mod mode; #[derive(Serialize, Deserialize, Debug, PartialEq)] diff --git a/src/wallet/inscribe/batchentry.rs b/src/wallet/inscribe/batch_entry.rs similarity index 100% rename from src/wallet/inscribe/batchentry.rs rename to src/wallet/inscribe/batch_entry.rs diff --git a/src/wallet/inscribe/batchfile.rs b/src/wallet/inscribe/batch_file.rs similarity index 100% rename from src/wallet/inscribe/batchfile.rs rename to src/wallet/inscribe/batch_file.rs From 20ff4fac3b9dfeffc9f2868aad534c3eec1e44fd Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 15:56:32 -0800 Subject: [PATCH 68/74] REsuse client --- src/wallet/inscribe/batch.rs | 44 +++++++++++++++--------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/wallet/inscribe/batch.rs b/src/wallet/inscribe/batch.rs index 2bb6dff8d1..0143835758 100644 --- a/src/wallet/inscribe/batch.rs +++ b/src/wallet/inscribe/batch.rs @@ -65,14 +65,14 @@ impl Batch { )))); } - let signed_commit_tx = wallet - .bitcoin_client()? + let bitcoin_client = wallet.bitcoin_client()?; + + let signed_commit_tx = bitcoin_client .sign_raw_transaction_with_wallet(&commit_tx, None, None)? .hex; let signed_reveal_tx = if self.parent_info.is_some() { - wallet - .bitcoin_client()? + bitcoin_client .sign_raw_transaction_with_wallet( &reveal_tx, Some( @@ -100,14 +100,9 @@ impl Batch { Self::backup_recovery_key(wallet, recovery_key_pair)?; } - let commit = wallet - .bitcoin_client()? - .send_raw_transaction(&signed_commit_tx)?; + let commit = bitcoin_client.send_raw_transaction(&signed_commit_tx)?; - let reveal = match wallet - .bitcoin_client()? - .send_raw_transaction(&signed_reveal_tx) - { + let reveal = match bitcoin_client.send_raw_transaction(&signed_reveal_tx) { Ok(txid) => txid, Err(err) => { return Err(anyhow!( @@ -452,21 +447,18 @@ impl Batch { wallet.chain().network(), ); - let info = wallet - .bitcoin_client()? - .get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; - - let response = wallet - .bitcoin_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("commit tx recovery key".to_string()), - })?; + let info = + bitcoin_client.get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; + + let response = bitcoin_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("commit tx recovery key".to_string()), + })?; for result in response { if !result.success { From 2e2a9ebe680b4a4419057713fcddb0aa624fe305 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:03:37 -0800 Subject: [PATCH 69/74] Use correct create wallet --- src/subcommand/wallet/send.rs | 2 +- src/wallet/inscribe/batch.rs | 2 + tests/balances.rs | 2 +- tests/decode.rs | 2 +- tests/etch.rs | 28 ++++----- tests/index.rs | 2 +- tests/json_api.rs | 16 ++--- tests/lib.rs | 14 +---- tests/runes.rs | 4 +- tests/server.rs | 22 +++---- tests/wallet/balance.rs | 8 +-- tests/wallet/cardinals.rs | 2 +- tests/wallet/inscribe.rs | 110 +++++++++++++++++----------------- tests/wallet/inscriptions.rs | 6 +- tests/wallet/outputs.rs | 6 +- tests/wallet/receive.rs | 9 ++- tests/wallet/sats.rs | 10 ++-- tests/wallet/send.rs | 54 ++++++++--------- tests/wallet/transactions.rs | 38 +++++++----- 19 files changed, 168 insertions(+), 169 deletions(-) diff --git a/src/subcommand/wallet/send.rs b/src/subcommand/wallet/send.rs index 7fb5bfb31b..ed25292b93 100644 --- a/src/subcommand/wallet/send.rs +++ b/src/subcommand/wallet/send.rs @@ -258,7 +258,7 @@ impl Send { }; let unsigned_transaction = - fund_raw_transaction(&bitcoin_client, fee_rate, &unfunded_transaction)?; + fund_raw_transaction(bitcoin_client, fee_rate, &unfunded_transaction)?; let signed_transaction = bitcoin_client .sign_raw_transaction_with_wallet(&unsigned_transaction, None, None)? diff --git a/src/wallet/inscribe/batch.rs b/src/wallet/inscribe/batch.rs index 0143835758..6a1238b4fe 100644 --- a/src/wallet/inscribe/batch.rs +++ b/src/wallet/inscribe/batch.rs @@ -447,6 +447,8 @@ impl Batch { wallet.chain().network(), ); + let bitcoin_client = wallet.bitcoin_client()?; + let info = bitcoin_client.get_descriptor_info(&format!("rawtr({})", recovery_private_key.to_wif()))?; diff --git a/tests/balances.rs b/tests/balances.rs index 092ecdf25e..4deb478dfc 100644 --- a/tests/balances.rs +++ b/tests/balances.rs @@ -43,7 +43,7 @@ fn with_runes() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let a = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let b = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); diff --git a/tests/decode.rs b/tests/decode.rs index 14e2bca769..04693aedb1 100644 --- a/tests/decode.rs +++ b/tests/decode.rs @@ -95,7 +95,7 @@ fn from_core() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); diff --git a/tests/etch.rs b/tests/etch.rs index 6b01aa5680..73226e3982 100644 --- a/tests/etch.rs +++ b/tests/etch.rs @@ -15,7 +15,7 @@ fn flag_is_required() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new(format!( "--regtest wallet etch --rune {} --divisibility 39 --fee-rate 1 --supply 1000 --symbol ยข", @@ -40,7 +40,7 @@ fn divisibility_over_max_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -68,7 +68,7 @@ fn supply_over_max_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -96,7 +96,7 @@ fn rune_below_minimum_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -124,7 +124,7 @@ fn reserved_rune_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -150,7 +150,7 @@ fn trying_to_etch_an_existing_rune_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -180,7 +180,7 @@ fn runes_can_be_etched() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -243,7 +243,7 @@ fn etch_sets_integer_fee_rate_correctly() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -279,7 +279,7 @@ fn etch_sets_decimal_fee_rate_correctly() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -315,7 +315,7 @@ fn etch_does_not_select_inscribed_utxos() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -365,7 +365,7 @@ fn inscribe_does_not_select_runic_utxos() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); @@ -410,7 +410,7 @@ fn send_amount_does_not_select_runic_utxos() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); @@ -445,7 +445,7 @@ fn send_satpoint_does_not_send_runic_utxos() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); @@ -480,7 +480,7 @@ fn send_inscription_does_not_select_runic_utxos() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); diff --git a/tests/index.rs b/tests/index.rs index c57b2bd210..9a9a3f3ab7 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -86,7 +86,7 @@ fn export_inscription_number_to_id_tsv() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let temp_dir = TempDir::new().unwrap(); diff --git a/tests/json_api.rs b/tests/json_api.rs index 243ce0e52c..15b283dc85 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -46,7 +46,7 @@ fn get_sat_with_inscription_and_sat_index() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription_id, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -87,7 +87,7 @@ fn get_sat_with_inscription_on_common_sat_and_more_inscriptions() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -146,7 +146,7 @@ fn get_inscription() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription_id, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -193,7 +193,7 @@ fn get_inscriptions() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let witness = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); @@ -250,7 +250,7 @@ fn get_inscriptions_in_block() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(10); @@ -303,7 +303,7 @@ fn get_output() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(3); let envelope = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); @@ -487,7 +487,7 @@ fn get_status() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -540,7 +540,7 @@ fn get_runes() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(3); diff --git a/tests/lib.rs b/tests/lib.rs index c442e9ef9b..54ac394178 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -57,10 +57,7 @@ const RUNE: u128 = 99246114928149462; type Inscribe = ord::wallet::inscribe::Output; type Etch = ord::subcommand::wallet::etch::Output; -fn create_wallet_new( - bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, - ord_rpc_server: &TestServer, -) { +fn create_wallet(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle, ord_rpc_server: &TestServer) { CommandBuilder::new(format!( "--chain {} wallet create", bitcoin_rpc_server.network() @@ -114,15 +111,6 @@ fn etch( output } -fn create_wallet(bitcoin_rpc_server: &test_bitcoincore_rpc::Handle) { - CommandBuilder::new(format!( - "--chain {} wallet create", - bitcoin_rpc_server.network() - )) - .bitcoin_rpc_server(bitcoin_rpc_server) - .run_and_deserialize_output::(); -} - fn envelope(payload: &[&[u8]]) -> bitcoin::Witness { let mut builder = bitcoin::script::Builder::new() .push_opcode(bitcoin::opcodes::OP_FALSE) diff --git a/tests/runes.rs b/tests/runes.rs index 013be0e9d3..2b3c529334 100644 --- a/tests/runes.rs +++ b/tests/runes.rs @@ -45,7 +45,7 @@ fn one_rune() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let etch = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -96,7 +96,7 @@ fn two_runes() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let a = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); let b = etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE + 1)); diff --git a/tests/server.rs b/tests/server.rs index 724929c516..2449fde82b 100644 --- a/tests/server.rs +++ b/tests/server.rs @@ -42,7 +42,7 @@ fn inscription_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -100,7 +100,7 @@ fn inscription_appears_on_reveal_transaction_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -117,7 +117,7 @@ fn multiple_inscriptions_appear_on_reveal_transaction_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -149,7 +149,7 @@ fn inscription_appears_on_output_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -166,7 +166,7 @@ fn inscription_page_after_send() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -203,7 +203,7 @@ fn inscription_content() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -249,7 +249,7 @@ fn inscription_metadata() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -286,7 +286,7 @@ fn inscriptions_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -307,7 +307,7 @@ fn inscriptions_page_is_sorted() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let mut regex = String::new(); @@ -324,7 +324,7 @@ fn inscriptions_page_has_next_and_previous() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (a, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); let (b, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -490,7 +490,7 @@ fn inscription_transactions_are_stored_with_transaction_index() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (_inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/balance.rs b/tests/wallet/balance.rs index 82458ddf89..5fa48f4e77 100644 --- a/tests/wallet/balance.rs +++ b/tests/wallet/balance.rs @@ -7,7 +7,7 @@ fn wallet_balance() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") @@ -42,7 +42,7 @@ fn inscribed_utxos_are_deducted_from_cardinal() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") @@ -87,7 +87,7 @@ fn runic_utxos_are_deducted_from_cardinal() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("--regtest --index-runes wallet balance") @@ -126,7 +126,7 @@ fn unsynced_wallet_fails_with_unindexed_output() { bitcoin_rpc_server.mine_blocks(1); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!( CommandBuilder::new("wallet balance") diff --git a/tests/wallet/cardinals.rs b/tests/wallet/cardinals.rs index a68d4d7da1..b2815b36ef 100644 --- a/tests/wallet/cardinals.rs +++ b/tests/wallet/cardinals.rs @@ -10,7 +10,7 @@ fn cardinals() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); inscribe(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/inscribe.rs b/tests/wallet/inscribe.rs index ec2a20e2f2..bbb5e2c873 100644 --- a/tests/wallet/inscribe.rs +++ b/tests/wallet/inscribe.rs @@ -13,7 +13,7 @@ fn inscribe_creates_inscriptions() { assert_eq!(bitcoin_rpc_server.descriptors().len(), 0); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, _) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -34,7 +34,7 @@ fn inscribe_works_with_huge_expensive_inscriptions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -52,7 +52,7 @@ fn metaprotocol_appears_on_inscription_page() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -93,7 +93,7 @@ fn inscribe_no_backup() { bitcoin_rpc_server.mine_blocks(1); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); assert_eq!(bitcoin_rpc_server.descriptors().len(), 2); @@ -111,7 +111,7 @@ fn inscribe_unknown_file_extension() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -132,7 +132,7 @@ fn inscribe_exceeds_chain_limit() { let ord_rpc_server = TestServer::spawn_with_args(&bitcoin_rpc_server, &["--signet"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("--chain signet wallet inscribe --file degenerate.png --fee-rate 1") .write("degenerate.png", [1; 1025]) @@ -154,7 +154,7 @@ fn regtest_has_no_content_size_limit() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -174,7 +174,7 @@ fn mainnet_has_no_content_size_limit() { let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -191,7 +191,7 @@ fn inscribe_does_not_use_inscribed_sats_as_cardinal_utxos() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 100); @@ -211,7 +211,7 @@ fn refuse_to_reinscribe_sats() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -235,7 +235,7 @@ fn refuse_to_inscribe_already_inscribed_utxo() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -266,7 +266,7 @@ fn inscribe_with_optional_satpoint_arg() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -299,7 +299,7 @@ fn inscribe_with_fee_rate() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -356,7 +356,7 @@ fn inscribe_with_commit_fee_rate() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -424,7 +424,7 @@ fn inscribe_with_dry_run_flag() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -451,7 +451,7 @@ fn inscribe_with_dry_run_flag_fees_increase() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -480,7 +480,7 @@ fn inscribe_to_specific_destination() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -513,7 +513,7 @@ fn inscribe_to_address_on_different_network() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -533,7 +533,7 @@ fn inscribe_with_no_limit() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -549,7 +549,7 @@ fn inscribe_works_with_postage() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); CommandBuilder::new("wallet inscribe --file foo.txt --postage 5btc --fee-rate 10".to_string()) @@ -574,7 +574,7 @@ fn inscribe_with_non_existent_parent_inscription() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -596,7 +596,7 @@ fn inscribe_with_parent_inscription_and_fee_rate() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = TestServer::spawn_with_json_api(&bitcoin_rpc_server); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -676,7 +676,7 @@ fn reinscribe_with_flag() { assert_eq!(bitcoin_rpc_server.descriptors().len(), 0); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let inscribe = CommandBuilder::new("wallet inscribe --file tulip.png --fee-rate 5.0 ") .write("tulip.png", [1; 520]) @@ -721,7 +721,7 @@ fn with_reinscribe_flag_but_not_actually_a_reinscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -751,7 +751,7 @@ fn try_reinscribe_without_flag() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -786,7 +786,7 @@ fn no_metadata_appears_on_inscription_page_if_no_metadata_is_passed() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -815,7 +815,7 @@ fn json_metadata_appears_on_inscription_page() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -844,7 +844,7 @@ fn cbor_metadata_appears_on_inscription_page() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -909,7 +909,7 @@ fn batch_inscribe_fails_if_batchfile_has_no_inscriptions() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -930,7 +930,7 @@ fn batch_inscribe_can_create_one_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -970,7 +970,7 @@ fn batch_inscribe_with_multiple_inscriptions() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1014,7 +1014,7 @@ fn batch_inscribe_with_multiple_inscriptions_with_parent() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1066,7 +1066,7 @@ fn batch_inscribe_respects_dry_run_flag() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1096,7 +1096,7 @@ fn batch_in_same_output_but_different_satpoints() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1164,7 +1164,7 @@ fn batch_in_same_output_with_non_default_postage() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1232,7 +1232,7 @@ fn batch_in_separate_outputs_with_parent() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1310,7 +1310,7 @@ fn batch_in_separate_outputs_with_parent_and_non_default_postage() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1389,7 +1389,7 @@ fn inscribe_does_not_pick_locked_utxos() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let coinbase_tx = &bitcoin_rpc_server.mine_blocks(1)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -1412,7 +1412,7 @@ fn inscribe_can_compress() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1476,7 +1476,7 @@ fn inscriptions_are_not_compressed_if_no_space_is_saved_by_compression() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1521,7 +1521,7 @@ fn batch_inscribe_fails_if_invalid_network_destination_address() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1542,7 +1542,7 @@ fn batch_inscribe_fails_with_shared_output_and_destination_set() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1564,7 +1564,7 @@ fn batch_inscribe_works_with_some_destinations_set_and_others_not() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1616,7 +1616,7 @@ fn batch_same_sat() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1682,7 +1682,7 @@ fn batch_same_sat_with_parent() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1769,7 +1769,7 @@ fn inscribe_with_sat_arg() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(2); @@ -1800,7 +1800,7 @@ fn inscribe_with_sat_arg_fails_if_no_index_or_not_found() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet inscribe --file foo.txt --sat 5010000000 --fee-rate 1") .write("foo.txt", "FOO") @@ -1833,7 +1833,7 @@ fn batch_inscribe_with_sat_argument_with_parent() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1880,7 +1880,7 @@ fn batch_inscribe_with_sat_arg_fails_if_wrong_mode() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -1909,7 +1909,7 @@ fn batch_inscribe_with_fee_rate() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(2); @@ -1968,7 +1968,7 @@ fn server_can_decompress_brotli() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -2032,7 +2032,7 @@ fn file_inscribe_with_delegate_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -2063,7 +2063,7 @@ fn file_inscribe_with_non_existent_delegate_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -2087,7 +2087,7 @@ fn batch_inscribe_with_delegate_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -2126,7 +2126,7 @@ fn batch_inscribe_with_non_existent_delegate_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); diff --git a/tests/wallet/inscriptions.rs b/tests/wallet/inscriptions.rs index 8fcd423970..e7ede054af 100644 --- a/tests/wallet/inscriptions.rs +++ b/tests/wallet/inscriptions.rs @@ -10,7 +10,7 @@ fn inscriptions() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -65,7 +65,7 @@ fn inscriptions_includes_locked_utxos() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -95,7 +95,7 @@ fn inscriptions_with_postage() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); diff --git a/tests/wallet/outputs.rs b/tests/wallet/outputs.rs index 784906f599..bb8202a141 100644 --- a/tests/wallet/outputs.rs +++ b/tests/wallet/outputs.rs @@ -7,7 +7,7 @@ fn outputs() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let coinbase_tx = &bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -29,7 +29,7 @@ fn outputs_includes_locked_outputs() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let coinbase_tx = &bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -53,7 +53,7 @@ fn outputs_includes_unbound_outputs() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let coinbase_tx = &bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); diff --git a/tests/wallet/receive.rs b/tests/wallet/receive.rs index 3b42e2a51c..d286fefe3d 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -2,11 +2,14 @@ use {super::*, ord::subcommand::wallet::receive}; #[test] fn receive() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let output = CommandBuilder::new("wallet receive") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::(); assert!(output.address.is_valid_for_network(Network::Bitcoin)); diff --git a/tests/wallet/sats.rs b/tests/wallet/sats.rs index 23211cc163..47936fe9c1 100644 --- a/tests/wallet/sats.rs +++ b/tests/wallet/sats.rs @@ -10,7 +10,7 @@ fn requires_sat_index() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet sats") .bitcoin_rpc_server(&bitcoin_rpc_server) @@ -30,7 +30,7 @@ fn sats() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let second_coinbase = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -53,7 +53,7 @@ fn sats_from_tsv_success() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let second_coinbase = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -77,7 +77,7 @@ fn sats_from_tsv_parse_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") .write("foo.tsv", "===") @@ -100,7 +100,7 @@ fn sats_from_tsv_file_not_found() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("--index-sats wallet sats --tsv foo.tsv") .bitcoin_rpc_server(&bitcoin_rpc_server) diff --git a/tests/wallet/send.rs b/tests/wallet/send.rs index 03c06f7a60..2d2ce4dc00 100644 --- a/tests/wallet/send.rs +++ b/tests/wallet/send.rs @@ -11,7 +11,7 @@ fn inscriptions_can_be_sent() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -59,7 +59,7 @@ fn send_unknown_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks(1)[0].txdata[0].txid(); @@ -80,7 +80,7 @@ fn send_inscribed_sat() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -136,7 +136,7 @@ fn send_addresses_must_be_valid_for_network() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000)[0].txdata[0].txid(); @@ -159,7 +159,7 @@ fn send_on_mainnnet_works_with_wallet_named_ord() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 1_000_000)[0].txdata[0].txid(); @@ -180,7 +180,7 @@ fn send_does_not_use_inscribed_sats_as_cardinal_utxos() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let txid = bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10_000)[0].txdata[0].txid(); CommandBuilder::new(format!( @@ -209,7 +209,7 @@ fn do_not_send_within_dust_limit_of_an_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (inscription, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -239,7 +239,7 @@ fn can_send_after_dust_limit_from_an_inscription() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -268,7 +268,7 @@ fn splitting_merged_inscriptions_is_possible() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(3); @@ -376,7 +376,7 @@ fn inscriptions_cannot_be_sent_by_satpoint() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let (_, reveal) = inscribe(&bitcoin_rpc_server, &ord_rpc_server); @@ -399,7 +399,7 @@ fn send_btc_with_fee_rate() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -446,7 +446,7 @@ fn send_btc_locks_inscriptions() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -482,7 +482,7 @@ fn send_btc_fails_if_lock_unspent_fails() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -501,7 +501,7 @@ fn wallet_send_with_fee_rate() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -538,7 +538,7 @@ fn user_must_provide_fee_rate_to_send() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -564,7 +564,7 @@ fn wallet_send_with_fee_rate_and_target_postage() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -602,7 +602,7 @@ fn send_btc_does_not_send_locked_utxos() { let ord_rpc_server = TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let coinbase_tx = &bitcoin_rpc_server.mine_blocks(1)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -629,7 +629,7 @@ fn sending_rune_that_has_not_been_etched_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); let coinbase_tx = &bitcoin_rpc_server.mine_blocks(1)[0].txdata[0]; let outpoint = OutPoint::new(coinbase_tx.txid(), 0); @@ -656,7 +656,7 @@ fn sending_rune_with_excessive_precision_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -683,7 +683,7 @@ fn sending_rune_with_insufficient_balance_is_an_error() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -710,7 +710,7 @@ fn sending_rune_works() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -762,7 +762,7 @@ fn sending_spaced_rune_works() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -813,7 +813,7 @@ fn sending_rune_with_divisibility_works() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks(1); @@ -888,7 +888,7 @@ fn sending_rune_leaves_unspent_runes_in_wallet() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -960,7 +960,7 @@ fn sending_rune_creates_transaction_with_expected_runestone() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -1043,7 +1043,7 @@ fn error_messages_use_spaced_runes() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); etch(&bitcoin_rpc_server, &ord_rpc_server, Rune(RUNE)); @@ -1076,7 +1076,7 @@ fn sending_rune_does_not_send_inscription() { &["--enable-json-api"], ); - create_wallet_new(&bitcoin_rpc_server, &ord_rpc_server); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); bitcoin_rpc_server.mine_blocks_with_subsidy(1, 10000); diff --git a/tests/wallet/transactions.rs b/tests/wallet/transactions.rs index 6bf29ada05..62968a539d 100644 --- a/tests/wallet/transactions.rs +++ b/tests/wallet/transactions.rs @@ -2,22 +2,25 @@ use {super::*, ord::subcommand::wallet::transactions::Output}; #[test] fn transactions() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); - assert!(rpc_server.loaded_wallets().is_empty()); + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); + + assert!(bitcoin_rpc_server.loaded_wallets().is_empty()); CommandBuilder::new("wallet transactions") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::>(); - assert_eq!(rpc_server.loaded_wallets().len(), 1); - assert_eq!(rpc_server.loaded_wallets().first().unwrap(), "ord"); + assert_eq!(bitcoin_rpc_server.loaded_wallets().len(), 1); + assert_eq!(bitcoin_rpc_server.loaded_wallets().first().unwrap(), "ord"); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet transactions") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[0].transaction.to_string(), "[[:xdigit:]]{64}"); @@ -26,34 +29,37 @@ fn transactions() { #[test] fn transactions_with_limit() { - let rpc_server = test_bitcoincore_rpc::spawn(); - create_wallet(&rpc_server); + let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); + let ord_rpc_server = + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + + create_wallet(&bitcoin_rpc_server, &ord_rpc_server); CommandBuilder::new("wallet transactions") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .stdout_regex(".*") .run_and_extract_stdout(); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet transactions") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[0].transaction.to_string(), "[[:xdigit:]]{64}"); assert_eq!(output[0].confirmations, 1); - rpc_server.mine_blocks(1); + bitcoin_rpc_server.mine_blocks(1); let output = CommandBuilder::new("wallet transactions") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[1].transaction.to_string(), "[[:xdigit:]]{64}"); assert_eq!(output[1].confirmations, 2); let output = CommandBuilder::new("wallet transactions --limit 1") - .bitcoin_rpc_server(&rpc_server) + .bitcoin_rpc_server(&bitcoin_rpc_server) .run_and_deserialize_output::>(); assert_regex_match!(output[0].transaction.to_string(), "[[:xdigit:]]{64}"); From ea7ba4e5d7712047d5746d7c8a540aac4cbe6242 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:06:35 -0800 Subject: [PATCH 70/74] Wallet --- tests/wallet/receive.rs | 2 +- tests/wallet/transactions.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/wallet/receive.rs b/tests/wallet/receive.rs index d286fefe3d..1d69f47190 100644 --- a/tests/wallet/receive.rs +++ b/tests/wallet/receive.rs @@ -4,7 +4,7 @@ use {super::*, ord::subcommand::wallet::receive}; fn receive() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); diff --git a/tests/wallet/transactions.rs b/tests/wallet/transactions.rs index 62968a539d..61eb85ac19 100644 --- a/tests/wallet/transactions.rs +++ b/tests/wallet/transactions.rs @@ -4,7 +4,7 @@ use {super::*, ord::subcommand::wallet::transactions::Output}; fn transactions() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); @@ -31,7 +31,7 @@ fn transactions() { fn transactions_with_limit() { let bitcoin_rpc_server = test_bitcoincore_rpc::spawn(); let ord_rpc_server = - TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--regtest"], &["--enable-json-api"]); + TestServer::spawn_with_server_args(&bitcoin_rpc_server, &[], &["--enable-json-api"]); create_wallet(&bitcoin_rpc_server, &ord_rpc_server); From b3cd66032002db70720c1cfc2d9084771564a149 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:07:11 -0800 Subject: [PATCH 71/74] Tweak --- tests/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib.rs b/tests/lib.rs index 54ac394178..3eb9ded79a 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -103,7 +103,7 @@ fn etch( ) ) .bitcoin_rpc_server(bitcoin_rpc_server) - .ord_rpc_server(ord_rpc_server) + .ord_rpc_server(ord_rpc_server) .run_and_deserialize_output(); bitcoin_rpc_server.mine_blocks(1); From 5ce1d28df14f3c7235a12ec17db120fb9db15628 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:08:58 -0800 Subject: [PATCH 72/74] Tweak --- tests/test_server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_server.rs b/tests/test_server.rs index f654e36add..40c70c859b 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -7,7 +7,8 @@ use { }; pub(crate) struct TestServer { - _index: Arc, + #[allow(unused)] + index: Arc, ord_rpc_url: String, ord_server_handle: Handle, port: u16, @@ -79,7 +80,7 @@ impl TestServer { } Self { - _index: index, + index, ord_rpc_url: bitcoin_rpc_server.url(), ord_server_handle, port, @@ -153,6 +154,5 @@ impl TestServer { impl Drop for TestServer { fn drop(&mut self) { self.ord_server_handle.shutdown(); - // self.child.kill().unwrap() } } From 4d1da88b126cd2b60facb7a325715bdd8f6ea082 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:10:28 -0800 Subject: [PATCH 73/74] Tweak --- tests/test_server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_server.rs b/tests/test_server.rs index 40c70c859b..6637d0653c 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -9,7 +9,7 @@ use { pub(crate) struct TestServer { #[allow(unused)] index: Arc, - ord_rpc_url: String, + bitcoin_rpc_url: String, ord_server_handle: Handle, port: u16, #[allow(unused)] @@ -81,7 +81,7 @@ impl TestServer { Self { index, - ord_rpc_url: bitcoin_rpc_server.url(), + bitcoin_rpc_url: bitcoin_rpc_server.url(), ord_server_handle, port, tempdir, @@ -131,7 +131,7 @@ impl TestServer { } pub(crate) fn sync_server(&self) { - let client = Client::new(&self.ord_rpc_url, Auth::None).unwrap(); + let client = Client::new(&self.bitcoin_rpc_url, Auth::None).unwrap(); let chain_block_count = client.get_block_count().unwrap() + 1; for i in 0.. { From fdeff8199dcb6603106ce4383aef5f473d51ae8a Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 22 Jan 2024 16:16:57 -0800 Subject: [PATCH 74/74] Tweak --- src/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet.rs b/src/wallet.rs index a333757717..df0308c46f 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -132,7 +132,7 @@ impl Wallet { } for output in utxos.keys() { - self.get_output(output)?; //check that wallet outputs in ord index + self.get_output(output)?; } Ok(utxos)